int txdata_test(void)
{
    enum { REPEAT = 4 };
    unsigned i, msgs;
    pj_timestamp usec[REPEAT], min, freq;
    int status;

    status = pj_get_timestamp_freq(&freq);
    if (status != PJ_SUCCESS)
	return status;

    status = core_txdata_test();
    if (status  != 0)
	return status;

#if INCLUDE_GCC_TEST
    status = gcc_test();
    if (status != 0)
	return status;
#endif

    status = txdata_test_uri_params();
    if (status != 0)
	return status;


    /*
     * Benchmark create_request()
     */
    PJ_LOG(3,(THIS_FILE, "   benchmarking request creation:"));
    for (i=0; i<REPEAT; ++i) {
	PJ_LOG(3,(THIS_FILE, "    test %d of %d..",
		  i+1, REPEAT));
	status = create_request_bench(&usec[i]);
	if (status != PJ_SUCCESS)
	    return status;
    }

    min.u64 = PJ_UINT64(0xFFFFFFFFFFFFFFF);
    for (i=0; i<REPEAT; ++i) {
	if (usec[i].u64 < min.u64) min.u64 = usec[i].u64;
    }

    msgs = (unsigned)(freq.u64 * LOOP / min.u64);

    PJ_LOG(3,(THIS_FILE, "    Requests created at %d requests/sec", msgs));

    report_ival("create-request-per-sec", 
		msgs, "msg/sec",
		"Number of typical request messages that can be created "
		"per second with <tt>pjsip_endpt_create_request()</tt>");


    /*
     * Benchmark create_response()
     */
    PJ_LOG(3,(THIS_FILE, "   benchmarking response creation:"));
    for (i=0; i<REPEAT; ++i) {
	PJ_LOG(3,(THIS_FILE, "    test %d of %d..",
		  i+1, REPEAT));
	status = create_response_bench(&usec[i]);
	if (status != PJ_SUCCESS)
	    return status;
    }

    min.u64 = PJ_UINT64(0xFFFFFFFFFFFFFFF);
    for (i=0; i<REPEAT; ++i) {
	if (usec[i].u64 < min.u64) min.u64 = usec[i].u64;
    }

    msgs = (unsigned)(freq.u64 * LOOP / min.u64);

    PJ_LOG(3,(THIS_FILE, "    Responses created at %d responses/sec", msgs));

    report_ival("create-response-per-sec", 
		msgs, "msg/sec",
		"Number of typical response messages that can be created "
		"per second with <tt>pjsip_endpt_create_response()</tt>");


    return 0;
}
Exemple #2
0
static pj_status_t init_report(void)
{
    char tmp[80];
    pj_time_val timestamp;
    pj_parsed_time date_time;
    pj_ssize_t len;
    pj_status_t status;
    
    pj_ansi_sprintf(tmp, "pjsip-static-bench-%s-%s.htm", PJ_OS_NAME, PJ_CC_NAME);

    status = pj_file_open(NULL, tmp, PJ_O_WRONLY, &fd_report);
    if (status != PJ_SUCCESS)
	return status;

    /* Title */
    len = pj_ansi_sprintf(buf, "<HTML>\n"
			       " <HEAD>\n"
			       "  <TITLE>PJSIP %s (%s) - Static Benchmark</TITLE>\n"
			       " </HEAD>\n"
			       "<BODY>\n"
			       "\n", 
			       PJ_VERSION,
			       (PJ_DEBUG ? "Debug" : "Release"));
    pj_file_write(fd_report, buf, &len);


    /* Title */
    len = pj_ansi_sprintf(buf, "<H1>PJSIP %s (%s) - Static Benchmark</H1>\n", 
			       PJ_VERSION,
			       (PJ_DEBUG ? "Debug" : "Release"));
    pj_file_write(fd_report, buf, &len);

    len = pj_ansi_sprintf(buf, "<P>Below is the benchmark result generated "
			       "by <b>test-pjsip</b> program. The program "
			       "is single-threaded only.</P>\n");
    pj_file_write(fd_report, buf, &len);


    /* Write table heading */
    len = pj_ansi_sprintf(buf, "<TABLE border=\"1\" cellpadding=\"4\">\n"
			       "  <TR><TD bgColor=\"aqua\" align=\"center\">Variable</TD>\n"
			       "      <TD bgColor=\"aqua\" align=\"center\">Value</TD>\n"
			       "      <TD bgColor=\"aqua\" align=\"center\">Description</TD>\n"
			       "  </TR>\n");
    pj_file_write(fd_report, buf, &len);


    /* Write version */
    report_sval("version", PJ_VERSION, "", "PJLIB/PJSIP version");


    /* Debug or release */
    report_sval("build-type", (PJ_DEBUG ? "Debug" : "Release"), "", "Build type");


    /* Write timestamp */
    pj_gettimeofday(&timestamp);
    report_ival("timestamp", timestamp.sec, "", "System timestamp of the test");


    /* Write time of day */
    pj_time_decode(&timestamp, &date_time);
    len = pj_ansi_sprintf(tmp, "%04d-%02d-%02d %02d:%02d:%02d",
			       date_time.year, date_time.mon+1, date_time.day,
			       date_time.hour, date_time.min, date_time.sec);
    report_sval("date-time", tmp, "", "Date/time of the test");


    /* Write System */
    report_sval("system", system_name, "", "System description");


    /* Write OS type */
    report_sval("os-family", PJ_OS_NAME, "", "Operating system family");


    /* Write CC name */
    len = pj_ansi_sprintf(tmp, "%s-%d.%d.%d", PJ_CC_NAME, 
			  PJ_CC_VER_1, PJ_CC_VER_2, PJ_CC_VER_2);
    report_sval("cc-name", tmp, "", "Compiler name and version");


    return PJ_SUCCESS;
}
Exemple #3
0
/*
 * UDP transport test.
 */
int transport_udp_test(void)
{
    enum { SEND_RECV_LOOP = 8 };
    pjsip_transport *udp_tp, *tp;
    pj_sockaddr_in addr, rem_addr;
    pj_str_t s;
    pj_status_t status;
    int rtt[SEND_RECV_LOOP], min_rtt;
    int i, pkt_lost;

    pj_sockaddr_in_init(&addr, NULL, TEST_UDP_PORT);

    /* Start UDP transport. */
    status = pjsip_udp_transport_start( endpt, &addr, NULL, 1, &udp_tp);
    if (status != PJ_SUCCESS) {
	app_perror("   Error: unable to start UDP transport", status);
	return -10;
    }

    /* UDP transport must have initial reference counter set to 1. */
    if (pj_atomic_get(udp_tp->ref_cnt) != 1)
	return -20;

    /* Test basic transport attributes */
    status = generic_transport_test(udp_tp);
    if (status != PJ_SUCCESS)
	return status;

    /* Test that transport manager is returning the correct
     * transport.
     */
    pj_sockaddr_in_init(&rem_addr, pj_cstr(&s, "1.1.1.1"), 80);
    status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_UDP, 
					   &rem_addr, sizeof(rem_addr),
					   NULL, &tp);
    if (status != PJ_SUCCESS)
	return -50;
    if (tp != udp_tp)
	return -60;

    /* pjsip_endpt_acquire_transport() adds reference, so we need
     * to decrement it.
     */
    pjsip_transport_dec_ref(tp);

    /* Check again that reference counter is 1. */
    if (pj_atomic_get(udp_tp->ref_cnt) != 1)
	return -70;

    /* Basic transport's send/receive loopback test. */
    pj_sockaddr_in_init(&rem_addr, pj_cstr(&s, "127.0.0.1"), TEST_UDP_PORT);
    for (i=0; i<SEND_RECV_LOOP; ++i) {
	status = transport_send_recv_test(PJSIP_TRANSPORT_UDP, tp, 
					  "sip:[email protected]:"TEST_UDP_PORT_STR,
					  &rtt[i]);
	if (status != 0)
	    return status;
    }

    min_rtt = 0xFFFFFFF;
    for (i=0; i<SEND_RECV_LOOP; ++i)
	if (rtt[i] < min_rtt) min_rtt = rtt[i];

    report_ival("udp-rtt-usec", min_rtt, "usec",
		"Best UDP transport round trip time, in microseconds "
		"(time from sending request until response is received. "
		"Tests were performed on local machine only)");


    /* Multi-threaded round-trip test. */
    status = transport_rt_test(PJSIP_TRANSPORT_UDP, tp, 
			       "sip:[email protected]:"TEST_UDP_PORT_STR, 
			       &pkt_lost);
    if (status != 0)
	return status;

    if (pkt_lost != 0)
	PJ_LOG(3,(THIS_FILE, "   note: %d packet(s) was lost", pkt_lost));

    /* Check again that reference counter is 1. */
    if (pj_atomic_get(udp_tp->ref_cnt) != 1)
	return -80;

    /* Destroy this transport. */
    pjsip_transport_dec_ref(udp_tp);

    /* Force destroy this transport. */
    status = pjsip_transport_destroy(udp_tp);
    if (status != PJ_SUCCESS)
	return -90;

    /* Flush events. */
    PJ_LOG(3,(THIS_FILE, "   Flushing events, 1 second..."));
    flush_events(1000);

    /* Done */
    return 0;
}
Exemple #4
0
int test_main(void)
{
    pj_status_t rc;
    pj_caching_pool caching_pool;
    const char *filename;
    unsigned tsx_test_cnt=0;
    struct tsx_test_param tsx_test[10];
    pj_status_t status;
#if INCLUDE_TSX_TEST
    unsigned i;
    pjsip_transport *tp;
#if PJ_HAS_TCP
    pjsip_tpfactory *tpfactory;
#endif	/* PJ_HAS_TCP */
#endif	/* INCLUDE_TSX_TEST */
    int line;

    pj_log_set_level(log_level);
    pj_log_set_decor(param_log_decor);

    if ((rc=pj_init()) != PJ_SUCCESS) {
	app_perror("pj_init", rc);
	return rc;
    }

    if ((rc=pjlib_util_init()) != PJ_SUCCESS) {
	app_perror("pj_init", rc);
	return rc;
    }

    status = init_report();
    if (status != PJ_SUCCESS)
	return status;

    pj_dump_config();

    pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 
			  PJSIP_TEST_MEM_SIZE );

    rc = pjsip_endpt_create(&caching_pool.factory, "endpt", &endpt);
    if (rc != PJ_SUCCESS) {
	app_perror("pjsip_endpt_create", rc);
	pj_caching_pool_destroy(&caching_pool);
	return rc;
    }

    PJ_LOG(3,(THIS_FILE,""));

    /* Init logger module. */
    init_msg_logger();
    msg_logger_set_enabled(1);

    /* Start transaction layer module. */
    rc = pjsip_tsx_layer_init_module(endpt);
    if (rc != PJ_SUCCESS) {
	app_perror("   Error initializing transaction module", rc);
	goto on_return;
    }

    /* Create loop transport. */
    rc = pjsip_loop_start(endpt, NULL);
    if (rc != PJ_SUCCESS) {
	app_perror("   error: unable to create datagram loop transport", 
		   rc);
	goto on_return;
    }
    tsx_test[tsx_test_cnt].port = 5060;
    tsx_test[tsx_test_cnt].tp_type = "loop-dgram";
    tsx_test[tsx_test_cnt].type = PJSIP_TRANSPORT_LOOP_DGRAM;
    ++tsx_test_cnt;


#if INCLUDE_URI_TEST
    DO_TEST(uri_test());
#endif

#if INCLUDE_MSG_TEST
    DO_TEST(msg_test());
    DO_TEST(msg_err_test());
#endif

#if INCLUDE_MULTIPART_TEST
    DO_TEST(multipart_test());
#endif

#if INCLUDE_TXDATA_TEST
    DO_TEST(txdata_test());
#endif

#if INCLUDE_TSX_BENCH
    DO_TEST(tsx_bench());
#endif

#if INCLUDE_UDP_TEST
    DO_TEST(transport_udp_test());
#endif

#if INCLUDE_LOOP_TEST
    DO_TEST(transport_loop_test());
#endif

#if INCLUDE_TCP_TEST
    DO_TEST(transport_tcp_test());
#endif

#if INCLUDE_RESOLVE_TEST
    DO_TEST(resolve_test());
#endif


#if INCLUDE_TSX_TEST
    status = pjsip_udp_transport_start(endpt, NULL, NULL, 1,  &tp);
    if (status == PJ_SUCCESS) {
	tsx_test[tsx_test_cnt].port = tp->local_name.port;
	tsx_test[tsx_test_cnt].tp_type = "udp";
	tsx_test[tsx_test_cnt].type = PJSIP_TRANSPORT_UDP;
	++tsx_test_cnt;
    }

#if PJ_HAS_TCP
    status = pjsip_tcp_transport_start(endpt, NULL, 1, &tpfactory);
    if (status == PJ_SUCCESS) {
	tsx_test[tsx_test_cnt].port = tpfactory->addr_name.port;
	tsx_test[tsx_test_cnt].tp_type = "tcp";
	tsx_test[tsx_test_cnt].type = PJSIP_TRANSPORT_TCP;
	++tsx_test_cnt;
    } else {
	app_perror("Unable to create TCP", status);
	rc = -4;
	goto on_return;
    }
#endif


    for (i=0; i<tsx_test_cnt; ++i) {
	DO_TSX_TEST(tsx_basic_test, &tsx_test[i]);
	DO_TSX_TEST(tsx_uac_test, &tsx_test[i]);
	DO_TSX_TEST(tsx_uas_test, &tsx_test[i]);
    }
#endif

#if INCLUDE_INV_OA_TEST
    DO_TEST(inv_offer_answer_test());
#endif

#if INCLUDE_REGC_TEST
    DO_TEST(regc_test());
#endif


on_return:
    flush_events(500);

    /* Dumping memory pool usage */
    PJ_LOG(3,(THIS_FILE, "Peak memory size=%u MB",
		         caching_pool.peak_used_size / 1000000));

    pjsip_endpt_destroy(endpt);
    pj_caching_pool_destroy(&caching_pool);

    PJ_LOG(3,(THIS_FILE, ""));
 
    pj_thread_get_stack_info(pj_thread_this(), &filename, &line);
    PJ_LOG(3,(THIS_FILE, "Stack max usage: %u, deepest: %s:%u", 
	              pj_thread_get_stack_max_usage(pj_thread_this()),
		      filename, line));
    if (rc == 0)
	PJ_LOG(3,(THIS_FILE, "Looks like everything is okay!.."));
    else
	PJ_LOG(3,(THIS_FILE, "Test completed with error(s)"));

    report_ival("test-status", rc, "", "Overall test status/result (0==success)");
    close_report();
    return rc;
}
Exemple #5
0
int tsx_bench(void)
{
    enum { WORKING_SET=10000, REPEAT = 4 };
    unsigned i, speed;
    pj_timestamp usec[REPEAT], min, freq;
    char desc[250];
    int status;

    status = pj_get_timestamp_freq(&freq);
    if (status != PJ_SUCCESS)
	return status;


    /*
     * Benchmark UAC
     */
    PJ_LOG(3,(THIS_FILE, "   benchmarking UAC transaction creation:"));
    for (i=0; i<REPEAT; ++i) {
	PJ_LOG(3,(THIS_FILE, "    test %d of %d..",
		  i+1, REPEAT));
	status = uac_tsx_bench(WORKING_SET, &usec[i]);
	if (status != PJ_SUCCESS)
	    return status;
    }

    min.u64 = PJ_UINT64(0xFFFFFFFFFFFFFFF);
    for (i=0; i<REPEAT; ++i) {
	if (usec[i].u64 < min.u64) min.u64 = usec[i].u64;
    }

    
    /* Report time */
    pj_ansi_sprintf(desc, "Time to create %d UAC transactions, in miliseconds",
			  WORKING_SET);
    report_ival("create-uac-time", (unsigned)(min.u64 * 1000 / freq.u64), "msec", desc);


    /* Write speed */
    speed = (unsigned)(freq.u64 * WORKING_SET / min.u64);
    PJ_LOG(3,(THIS_FILE, "    UAC created at %d tsx/sec", speed));

    pj_ansi_sprintf(desc, "Number of UAC transactions that potentially can be created per second "
			  "with <tt>pjsip_tsx_create_uac()</tt>, based on the time "
			  "to create %d simultaneous transactions above.",
			  WORKING_SET);

    report_ival("create-uac-tsx-per-sec", 
		speed, "tsx/sec", desc);



    /*
     * Benchmark UAS
     */
    PJ_LOG(3,(THIS_FILE, "   benchmarking UAS transaction creation:"));
    for (i=0; i<REPEAT; ++i) {
	PJ_LOG(3,(THIS_FILE, "    test %d of %d..",
		  i+1, REPEAT));
	status = uas_tsx_bench(WORKING_SET, &usec[i]);
	if (status != PJ_SUCCESS)
	    return status;
    }

    min.u64 = PJ_UINT64(0xFFFFFFFFFFFFFFF);
    for (i=0; i<REPEAT; ++i) {
	if (usec[i].u64 < min.u64) min.u64 = usec[i].u64;
    }

    
    /* Report time */
    pj_ansi_sprintf(desc, "Time to create %d UAS transactions, in miliseconds",
			  WORKING_SET);
    report_ival("create-uas-time", (unsigned)(min.u64 * 1000 / freq.u64), "msec", desc);


    /* Write speed */
    speed = (unsigned)(freq.u64 * WORKING_SET / min.u64);
    PJ_LOG(3,(THIS_FILE, "    UAS created at %d tsx/sec", speed));

    pj_ansi_sprintf(desc, "Number of UAS transactions that potentially can be created per second "
			  "with <tt>pjsip_tsx_create_uas()</tt>, based on the time "
			  "to create %d simultaneous transactions above.",
			  WORKING_SET);

    report_ival("create-uas-tsx-per-sec", 
		speed, "tsx/sec", desc);

    return PJ_SUCCESS;
}
Exemple #6
0
int uri_test(void)
{
    enum { COUNT = 1, DETECT=0, PARSE=1, PRINT=2 };
    struct {
	unsigned parse;
	unsigned print;
	unsigned cmp;
    } run[COUNT];
    unsigned i, max;
    pj_ssize_t avg_len;
    char desc[200];
    pj_status_t status;

    status = simple_uri_test();
    if (status != PJ_SUCCESS)
	return status;

#if INCLUDE_BENCHMARKS
    for (i=0; i<COUNT; ++i) {
	PJ_LOG(3,(THIS_FILE, "  benchmarking (%d of %d)...", i+1, COUNT));
	status = uri_benchmark(&run[i].parse, &run[i].print, &run[i].cmp);
	if (status != PJ_SUCCESS)
	    return status;
    }

    /* Calculate average URI length */
    for (i=0, avg_len=0; i<PJ_ARRAY_SIZE(uri_test_array); ++i) {
	avg_len += uri_test_array[i].len;
    }
    avg_len /= PJ_ARRAY_SIZE(uri_test_array);


    /* 
     * Print maximum parse/sec 
     */
    for (i=0, max=0; i<COUNT; ++i)
	if (run[i].parse > max) max = run[i].parse;

    PJ_LOG(3,("", "  Maximum URI parse/sec=%u", max));

    pj_ansi_sprintf(desc, "Number of SIP/TEL URIs that can be <B>parsed</B> with "
			  "<tt>pjsip_parse_uri()</tt> per second "
			  "(tested with %d URI set, with average length of "
			  "%d chars)",
			  (int)PJ_ARRAY_SIZE(uri_test_array), (int)avg_len);

    report_ival("uri-parse-per-sec", max, "URI/sec", desc);

    /* URI parsing bandwidth */
    report_ival("uri-parse-bandwidth-mb", (int)avg_len*max/1000000, "MB/sec",
	        "URI parsing bandwidth in megabytes (number of megabytes "
		"worth of URI that can be parsed per second)");


    /* Print maximum print/sec */
    for (i=0, max=0; i<COUNT; ++i)
	if (run[i].print > max) max = run[i].print;

    PJ_LOG(3,("", "  Maximum URI print/sec=%u", max));

    pj_ansi_sprintf(desc, "Number of SIP/TEL URIs that can be <B>printed</B> with "
			  "<tt>pjsip_uri_print()</tt> per second "
			  "(tested with %d URI set, with average length of "
			  "%d chars)",
			  (int)PJ_ARRAY_SIZE(uri_test_array), (int)avg_len);

    report_ival("uri-print-per-sec", max, "URI/sec", desc);

    /* Print maximum detect/sec */
    for (i=0, max=0; i<COUNT; ++i)
	if (run[i].cmp > max) max = run[i].cmp;

    PJ_LOG(3,("", "  Maximum URI comparison/sec=%u", max));

    pj_ansi_sprintf(desc, "Number of SIP/TEL URIs that can be <B>compared</B> with "
			  "<tt>pjsip_uri_cmp()</tt> per second "
			  "(tested with %d URI set, with average length of "
			  "%d chars)",
			  (int)PJ_ARRAY_SIZE(uri_test_array), (int)avg_len);

    report_ival("uri-cmp-per-sec", max, "URI/sec", desc);

#endif	/* INCLUDE_BENCHMARKS */

    return PJ_SUCCESS;
}
static int datagram_loop_test()
{
    enum { LOOP = 8 };
    pjsip_transport *loop;
    int i, pkt_lost;
    pj_sockaddr_in addr;
    pj_status_t status;
    long ref_cnt;
    int rtt[LOOP], min_rtt;

    PJ_LOG(3,(THIS_FILE, "testing datagram loop transport"));

    /* Test acquire transport. */
    status = pjsip_endpt_acquire_transport( endpt, PJSIP_TRANSPORT_LOOP_DGRAM,
					    &addr, sizeof(addr), NULL, &loop);
    if (status != PJ_SUCCESS) {
	app_perror("   error: loop transport is not configured", status);
	return -20;
    }

    /* Get initial reference counter */
    ref_cnt = pj_atomic_get(loop->ref_cnt);

    /* Test basic transport attributes */
    status = generic_transport_test(loop);
    if (status != PJ_SUCCESS)
	return status;

    /* Basic transport's send/receive loopback test. */
    for (i=0; i<LOOP; ++i) {
	status = transport_send_recv_test(PJSIP_TRANSPORT_LOOP_DGRAM, loop, 
					  "sip:[email protected];transport=loop-dgram",
					  &rtt[i]);
	if (status != 0)
	    return status;
    }

    min_rtt = 0xFFFFFFF;
    for (i=0; i<LOOP; ++i)
	if (rtt[i] < min_rtt) min_rtt = rtt[i];

    report_ival("loop-rtt-usec", min_rtt, "usec",
		"Best Loopback transport round trip time, in microseconds "
		"(time from sending request until response is received. "
		"Tests were performed on local machine only)");


    /* Multi-threaded round-trip test. */
    status = transport_rt_test(PJSIP_TRANSPORT_LOOP_DGRAM, loop, 
			       "sip:[email protected];transport=loop-dgram",
			       &pkt_lost);
    if (status != 0)
	return status;

    if (pkt_lost != 0) {
	PJ_LOG(3,(THIS_FILE, "   error: %d packet(s) was lost", pkt_lost));
	return -40;
    }

    /* Put delay. */
    PJ_LOG(3,(THIS_FILE,"  setting network delay to 10 ms"));
    pjsip_loop_set_delay(loop, 10);

    /* Multi-threaded round-trip test. */
    status = transport_rt_test(PJSIP_TRANSPORT_LOOP_DGRAM, loop, 
			       "sip:[email protected];transport=loop-dgram",
			       &pkt_lost);
    if (status != 0)
	return status;

    if (pkt_lost != 0) {
	PJ_LOG(3,(THIS_FILE, "   error: %d packet(s) was lost", pkt_lost));
	return -50;
    }

    /* Restore delay. */
    pjsip_loop_set_delay(loop, 0);

    /* Check reference counter. */
    if (pj_atomic_get(loop->ref_cnt) != ref_cnt) {
	PJ_LOG(3,(THIS_FILE, "   error: ref counter is not %d (%d)", 
			     ref_cnt, pj_atomic_get(loop->ref_cnt)));
	return -51;
    }

    /* Decrement reference. */
    pjsip_transport_dec_ref(loop);

    return 0;
}
int transport_tcp_test(void)
{
    enum { SEND_RECV_LOOP = 8 };
    pjsip_tpfactory *tpfactory;
    pjsip_transport *tcp;
    pj_sockaddr_in rem_addr;
    pj_status_t status;
    char url[PJSIP_MAX_URL_SIZE];
    int rtt[SEND_RECV_LOOP], min_rtt;
    int i, pkt_lost;

    /* Start TCP listener on arbitrary port. */
    status = pjsip_tcp_transport_start(endpt, NULL, 1, &tpfactory);
    if (status != PJ_SUCCESS) {
	app_perror("   Error: unable to start TCP transport", status);
	return -10;
    }


    /* Get the listener address */
    status = pj_sockaddr_in_init(&rem_addr, &tpfactory->addr_name.host,
				 (pj_uint16_t)tpfactory->addr_name.port);
    if (status != PJ_SUCCESS) {
	app_perror("   Error: possibly invalid TCP address name", status);
	return -14;
    }

    pj_ansi_sprintf(url, "sip:alice@%s:%d;transport=tcp",
		    pj_inet_ntoa(rem_addr.sin_addr),
		    pj_ntohs(rem_addr.sin_port));


    /* Acquire one TCP transport. */
    status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_TCP, 
					   &rem_addr, sizeof(rem_addr),
					   NULL, &tcp);
    if (status != PJ_SUCCESS || tcp == NULL) {
	app_perror("   Error: unable to acquire TCP transport", status);
	return -17;
    }

    /* After pjsip_endpt_acquire_transport, TCP transport must have
     * reference counter 1. 
     */
    if (pj_atomic_get(tcp->ref_cnt) != 1)
	return -20;

    /* Test basic transport attributes */
    status = generic_transport_test(tcp);
    if (status != PJ_SUCCESS)
	return status;


    /* Check again that reference counter is 1. */
    if (pj_atomic_get(tcp->ref_cnt) != 1)
	return -40;

    /* Load test */
    if (transport_load_test(url) != 0)
	return -60;

    /* Basic transport's send/receive loopback test. */
    for (i=0; i<SEND_RECV_LOOP; ++i) {
	status = transport_send_recv_test(PJSIP_TRANSPORT_TCP, tcp, url, &rtt[i]);

	if (status != 0) {
	    pjsip_transport_dec_ref(tcp);
	    flush_events(500);
	    return -72;
	}
    }

    min_rtt = 0xFFFFFFF;
    for (i=0; i<SEND_RECV_LOOP; ++i)
	if (rtt[i] < min_rtt) min_rtt = rtt[i];

    report_ival("tcp-rtt-usec", min_rtt, "usec",
		"Best TCP transport round trip time, in microseconds "
		"(time from sending request until response is received. "
		"Tests were performed on local machine only, and after "
		"TCP socket has been established by previous test)");


    /* Multi-threaded round-trip test. */
    status = transport_rt_test(PJSIP_TRANSPORT_TCP, tcp, url, &pkt_lost);
    if (status != 0) {
	pjsip_transport_dec_ref(tcp);
	return status;
    }

    if (pkt_lost != 0)
	PJ_LOG(3,(THIS_FILE, "   note: %d packet(s) was lost", pkt_lost));

    /* Check again that reference counter is still 1. */
    if (pj_atomic_get(tcp->ref_cnt) != 1)
	return -80;

    /* Destroy this transport. */
    pjsip_transport_dec_ref(tcp);

    /* Force destroy this transport. */
    status = pjsip_transport_destroy(tcp);
    if (status != PJ_SUCCESS)
	return -90;

    /* Unregister factory */
    status = pjsip_tpmgr_unregister_tpfactory(pjsip_endpt_get_tpmgr(endpt), 
					      tpfactory);
    if (status != PJ_SUCCESS)
	return -95;

    /* Flush events. */
    PJ_LOG(3,(THIS_FILE, "   Flushing events, 1 second..."));
    flush_events(1000);

    /* Done */
    return 0;
}