|
Home --> Documentations --> PJMEDIA Reference
pjsip-perf is a complete program to measure the performance of PJSIP or other SIP endpoints. It consists of two parts:
- the server, to respond incoming requests, and
- the client, who actively submits requests and measure the performance of the server.
Both server and client part can run simultaneously, to measure the performance when both endpoints are co-located in a single program.
The server accepts both INVITE and non-INVITE requests. The server exports several different types of URL, which would control how the request would be handled by the server:
- URL with "0" as the user part will be handled statelessly. It should not be used with INVITE method.
- URL with "1" as the user part will be handled statefully. If the request is an INVITE request, INVITE transaction will be created and 200/OK response will be sent, along with a valid SDP body. However, the SDP is just a static text body, and is not a proper SDP generated by PJMEDIA.
- URL with "2" as the user part is only meaningful for INVITE requests, as it would be handled call-statefully by the server. For this URL, the server also would generate SDP dynamically and perform a proper SDP negotiation for the incoming call. Also for every call, server will limit the call duration to 10 seconds, on which the call will be terminated if the client doesn't hangup the call.
This file is pjsip-apps/src/samples/pjsip-perf.c
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00060
00061 #include <pjsip.h>
00062 #include <pjmedia.h>
00063 #include <pjmedia-codec.h>
00064 #include <pjsip_ua.h>
00065 #include <pjsip_simple.h>
00066 #include <pjlib-util.h>
00067 #include <pjlib.h>
00068 #include <stdio.h>
00069
00070 #if defined(PJ_WIN32) && PJ_WIN32!=0
00071 # include <windows.h>
00072 #endif
00073
00074 #define THIS_FILE "pjsip-perf.c"
00075 #define DEFAULT_COUNT (pjsip_cfg()->tsx.max_count/2>10000?10000:pjsip_cfg()->tsx.max_count/2)
00076 #define JOB_WINDOW 1000
00077 #define TERMINATE_TSX(x,c)
00078
00079
00080 #ifndef CACHING_POOL_SIZE
00081 # define CACHING_POOL_SIZE (256*1024*1024)
00082 #endif
00083
00084
00085
00086
00087
00088
00089 static pj_str_t dummy_sdp_str =
00090 {
00091 "v=0\r\n"
00092 "o=- 3360842071 3360842071 IN IP4 192.168.0.68\r\n"
00093 "s=pjmedia\r\n"
00094 "c=IN IP4 192.168.0.68\r\n"
00095 "t=0 0\r\n"
00096 "m=audio 4000 RTP/AVP 0 8 3 103 102 101\r\n"
00097 "a=rtcp:4001 IN IP4 192.168.0.68\r\n"
00098 "a=rtpmap:103 speex/16000\r\n"
00099 "a=rtpmap:102 speex/8000\r\n"
00100 "a=rtpmap:3 GSM/8000\r\n"
00101 "a=rtpmap:0 PCMU/8000\r\n"
00102 "a=rtpmap:8 PCMA/8000\r\n"
00103 "a=sendrecv\r\n"
00104 "a=rtpmap:101 telephone-event/8000\r\n"
00105 "a=fmtp:101 0-15\r\n",
00106 0
00107 };
00108
00109 static pj_str_t mime_application = { "application", 11};
00110 static pj_str_t mime_sdp = {"sdp", 3};
00111
00112
00113 struct srv_state
00114 {
00115 unsigned stateless_cnt;
00116 unsigned stateful_cnt;
00117 unsigned call_cnt;
00118 };
00119
00120
00121 struct app
00122 {
00123 pj_caching_pool cp;
00124 pj_pool_t *pool;
00125 pj_bool_t use_tcp;
00126 pj_str_t local_addr;
00127 int local_port;
00128 pjsip_endpoint *sip_endpt;
00129 pjmedia_endpt *med_endpt;
00130 pj_str_t local_uri;
00131 pj_str_t local_contact;
00132 unsigned skinfo_cnt;
00133 pjmedia_sock_info skinfo[8];
00134
00135 pj_bool_t thread_quit;
00136 unsigned thread_count;
00137 pj_thread_t *thread[16];
00138
00139 pj_bool_t real_sdp;
00140 pjmedia_sdp_session *dummy_sdp;
00141
00142 int log_level;
00143
00144 struct {
00145 pjsip_method method;
00146 pj_str_t dst_uri;
00147 pj_bool_t stateless;
00148 unsigned timeout;
00149 unsigned job_count,
00150 job_submitted,
00151 job_finished,
00152 job_window;
00153 unsigned stat_max_window;
00154 pj_time_val first_request;
00155 pj_time_val requests_sent;
00156 pj_time_val last_completion;
00157 unsigned total_responses;
00158 unsigned response_codes[800];
00159 } client;
00160
00161 struct {
00162 pj_bool_t send_trying;
00163 pj_bool_t send_ringing;
00164 unsigned delay;
00165 struct srv_state prev_state;
00166 struct srv_state cur_state;
00167 } server;
00168
00169
00170 } app;
00171
00172 struct call
00173 {
00174 pjsip_inv_session *inv;
00175 pj_timer_entry ans_timer;
00176 };
00177
00178
00179 static void app_perror(const char *sender, const char *title,
00180 pj_status_t status)
00181 {
00182 char errmsg[PJ_ERR_MSG_SIZE];
00183
00184 pj_strerror(status, errmsg, sizeof(errmsg));
00185 PJ_LOG(1,(sender, "%s: %s [code=%d]", title, errmsg, status));
00186 }
00187
00188
00189
00190
00191
00192 static pj_bool_t mod_stateless_on_rx_request(pjsip_rx_data *rdata);
00193
00194
00195
00196 static pjsip_module mod_stateless_server =
00197 {
00198 NULL, NULL,
00199 { "mod-stateless-server", 20 },
00200 -1,
00201 PJSIP_MOD_PRIORITY_APPLICATION,
00202 NULL,
00203 NULL,
00204 NULL,
00205 NULL,
00206 &mod_stateless_on_rx_request,
00207 NULL,
00208 NULL,
00209 NULL,
00210 NULL,
00211 };
00212
00213
00214 static pj_bool_t mod_stateless_on_rx_request(pjsip_rx_data *rdata)
00215 {
00216 const pj_str_t stateless_user = { "0", 1 };
00217 pjsip_uri *uri;
00218 pjsip_sip_uri *sip_uri;
00219
00220 uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
00221
00222
00223 if (!PJSIP_URI_SCHEME_IS_SIP(uri))
00224 return PJ_FALSE;
00225
00226 sip_uri = (pjsip_sip_uri*) uri;
00227
00228
00229 if (pj_strcmp(&sip_uri->user, &stateless_user)!=0)
00230 return PJ_FALSE;
00231
00232
00233
00234
00235
00236
00237 if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD)
00238 return PJ_TRUE;
00239
00240
00241
00242
00243 pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 200, NULL,
00244 NULL, NULL);
00245 app.server.cur_state.stateless_cnt++;
00246 return PJ_TRUE;
00247 }
00248
00249
00250
00251
00252
00253 static pj_bool_t mod_stateful_on_rx_request(pjsip_rx_data *rdata);
00254
00255
00256
00257 static pjsip_module mod_stateful_server =
00258 {
00259 NULL, NULL,
00260 { "mod-stateful-server", 19 },
00261 -1,
00262 PJSIP_MOD_PRIORITY_APPLICATION,
00263 NULL,
00264 NULL,
00265 NULL,
00266 NULL,
00267 &mod_stateful_on_rx_request,
00268 NULL,
00269 NULL,
00270 NULL,
00271 NULL,
00272 };
00273
00274
00275 static pj_bool_t mod_stateful_on_rx_request(pjsip_rx_data *rdata)
00276 {
00277 const pj_str_t stateful_user = { "1", 1 };
00278 pjsip_uri *uri;
00279 pjsip_sip_uri *sip_uri;
00280
00281 uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
00282
00283
00284 if (!PJSIP_URI_SCHEME_IS_SIP(uri))
00285 return PJ_FALSE;
00286
00287 sip_uri = (pjsip_sip_uri*) uri;
00288
00289
00290 if (pj_strcmp(&sip_uri->user, &stateful_user)!=0)
00291 return PJ_FALSE;
00292
00293
00294
00295
00296
00297 switch (rdata->msg_info.msg->line.req.method.id) {
00298 case PJSIP_INVITE_METHOD:
00299 {
00300 pjsip_msg_body *body;
00301
00302 if (dummy_sdp_str.slen == 0)
00303 dummy_sdp_str.slen = pj_ansi_strlen(dummy_sdp_str.ptr);
00304
00305 body = pjsip_msg_body_create(rdata->tp_info.pool,
00306 &mime_application, &mime_sdp,
00307 &dummy_sdp_str);
00308 pjsip_endpt_respond(app.sip_endpt, &mod_stateful_server, rdata,
00309 200, NULL, NULL, body, NULL);
00310 }
00311 break;
00312 case PJSIP_ACK_METHOD:
00313 return PJ_TRUE;
00314 default:
00315 pjsip_endpt_respond(app.sip_endpt, &mod_stateful_server, rdata,
00316 200, NULL, NULL, NULL, NULL);
00317 break;
00318 }
00319
00320 app.server.cur_state.stateful_cnt++;
00321 return PJ_TRUE;
00322 }
00323
00324
00325
00326
00327
00328 static pj_bool_t mod_call_on_rx_request(pjsip_rx_data *rdata);
00329
00330
00331
00332 static pjsip_module mod_call_server =
00333 {
00334 NULL, NULL,
00335 { "mod-call-server", 15 },
00336 -1,
00337 PJSIP_MOD_PRIORITY_APPLICATION,
00338 NULL,
00339 NULL,
00340 NULL,
00341 NULL,
00342 &mod_call_on_rx_request,
00343 NULL,
00344 NULL,
00345 NULL,
00346 NULL,
00347 };
00348
00349
00350 static pj_status_t send_response(pjsip_inv_session *inv,
00351 pjsip_rx_data *rdata,
00352 int code,
00353 pj_bool_t *has_initial)
00354 {
00355 pjsip_tx_data *tdata;
00356 pj_status_t status;
00357
00358 if (*has_initial) {
00359 status = pjsip_inv_answer(inv, code, NULL, NULL, &tdata);
00360 } else {
00361 status = pjsip_inv_initial_answer(inv, rdata, code,
00362 NULL, NULL, &tdata);
00363 }
00364
00365 if (status != PJ_SUCCESS) {
00366 if (*has_initial) {
00367 status = pjsip_inv_answer(inv, PJSIP_SC_NOT_ACCEPTABLE,
00368 NULL, NULL, &tdata);
00369 } else {
00370 status = pjsip_inv_initial_answer(inv, rdata,
00371 PJSIP_SC_NOT_ACCEPTABLE,
00372 NULL, NULL, &tdata);
00373 }
00374
00375 if (status == PJ_SUCCESS) {
00376 *has_initial = PJ_TRUE;
00377 pjsip_inv_send_msg(inv, tdata);
00378 } else {
00379 pjsip_inv_terminate(inv, 500, PJ_FALSE);
00380 return -1;
00381 }
00382 } else {
00383 *has_initial = PJ_TRUE;
00384
00385 status = pjsip_inv_send_msg(inv, tdata);
00386 if (status != PJ_SUCCESS) {
00387 pjsip_tx_data_dec_ref(tdata);
00388 return status;
00389 }
00390 }
00391
00392 return status;
00393 }
00394
00395 static void answer_timer_cb(pj_timer_heap_t *h, pj_timer_entry *entry)
00396 {
00397 struct call *call = entry->user_data;
00398 pj_bool_t has_initial = PJ_TRUE;
00399
00400 PJ_UNUSED_ARG(h);
00401
00402 entry->id = 0;
00403 send_response(call->inv, NULL, 200, &has_initial);
00404 }
00405
00406 static pj_bool_t mod_call_on_rx_request(pjsip_rx_data *rdata)
00407 {
00408 const pj_str_t call_user = { "2", 1 };
00409 pjsip_uri *uri;
00410 pjsip_sip_uri *sip_uri;
00411 struct call *call;
00412 pjsip_dialog *dlg;
00413 pjmedia_sdp_session *sdp;
00414 pjsip_tx_data *tdata;
00415 pj_bool_t has_initial = PJ_FALSE;
00416 pj_status_t status;
00417
00418 uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
00419
00420
00421 if (!PJSIP_URI_SCHEME_IS_SIP(uri))
00422 return PJ_FALSE;
00423
00424 sip_uri = (pjsip_sip_uri*) uri;
00425
00426
00427 if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) {
00428 return PJ_FALSE;
00429 }
00430
00431
00432
00433
00434
00435
00436
00437 if (pj_strcmp(&sip_uri->user, &call_user) == 0 ||
00438 sip_uri->user.slen != 1 ||
00439 (*sip_uri->user.ptr != '0' && *sip_uri->user.ptr != '1'))
00440 {
00441
00442
00443 } else {
00444 return PJ_FALSE;
00445 }
00446
00447
00448
00449 if (app.real_sdp) {
00450 unsigned options = 0;
00451 status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
00452 app.sip_endpt, &tdata);
00453 if (status != PJ_SUCCESS) {
00454
00455
00456
00457
00458
00459 if (tdata) {
00460 pjsip_response_addr res_addr;
00461
00462 pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
00463 pjsip_endpt_send_response(app.sip_endpt, &res_addr, tdata,
00464 NULL, NULL);
00465
00466 } else {
00467
00468
00469 pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 500, NULL,
00470 NULL, NULL);
00471 }
00472
00473 return PJ_TRUE;
00474 }
00475 }
00476
00477
00478 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
00479 &app.local_contact, &dlg);
00480 if (status != PJ_SUCCESS) {
00481 const pj_str_t reason = pj_str("Unable to create dialog");
00482 pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
00483 500, &reason,
00484 NULL, NULL);
00485 return PJ_TRUE;
00486 }
00487
00488
00489 call = pj_pool_zalloc(dlg->pool, sizeof(struct call));
00490
00491
00492 if (app.real_sdp) {
00493 status = pjmedia_endpt_create_sdp(app.med_endpt, rdata->tp_info.pool,
00494 app.skinfo_cnt, app.skinfo,
00495 &sdp);
00496 } else {
00497 sdp = app.dummy_sdp;
00498 }
00499
00500
00501 status = pjsip_inv_create_uas( dlg, rdata, sdp, 0, &call->inv);
00502 if (status != PJ_SUCCESS) {
00503 pjsip_dlg_create_response(dlg, rdata, 500, NULL, &tdata);
00504 pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata);
00505 return PJ_TRUE;
00506 }
00507
00508
00509 if (app.server.send_trying) {
00510 status = send_response(call->inv, rdata, 100, &has_initial);
00511 if (status != PJ_SUCCESS)
00512 return PJ_TRUE;
00513 }
00514
00515
00516 if (app.server.send_ringing) {
00517 status = send_response(call->inv, rdata, 180, &has_initial);
00518 if (status != PJ_SUCCESS)
00519 return PJ_TRUE;
00520 }
00521
00522
00523 if (app.server.delay) {
00524 pj_time_val delay;
00525
00526 call->ans_timer.id = 1;
00527 call->ans_timer.user_data = call;
00528 call->ans_timer.cb = &answer_timer_cb;
00529
00530 delay.sec = 0;
00531 delay.msec = app.server.delay;
00532 pj_time_val_normalize(&delay);
00533
00534 pjsip_endpt_schedule_timer(app.sip_endpt, &call->ans_timer, &delay);
00535
00536 } else {
00537
00538 status = send_response(call->inv, rdata, 200, &has_initial);
00539 PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return PJ_TRUE);
00540 }
00541
00542
00543 app.server.cur_state.call_cnt++;
00544
00545 return PJ_TRUE;
00546 }
00547
00548
00549
00550
00551
00552
00553
00554 static pj_bool_t mod_responder_on_rx_request(pjsip_rx_data *rdata);
00555
00556
00557
00558 static pjsip_module mod_responder =
00559 {
00560 NULL, NULL,
00561 { "mod-responder", 13 },
00562 -1,
00563 PJSIP_MOD_PRIORITY_APPLICATION+1,
00564 NULL,
00565 NULL,
00566 NULL,
00567 NULL,
00568 &mod_responder_on_rx_request,
00569 NULL,
00570 NULL,
00571 NULL,
00572 NULL,
00573 };
00574
00575
00576 static pj_bool_t mod_responder_on_rx_request(pjsip_rx_data *rdata)
00577 {
00578 const pj_str_t reason = pj_str("Not expecting request at this URI");
00579
00580
00581
00582
00583 if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
00584 pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 500, &reason,
00585 NULL, NULL);
00586 }
00587
00588 return PJ_TRUE;
00589 }
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599 static pj_bool_t logger_on_rx_msg(pjsip_rx_data *rdata)
00600 {
00601 PJ_LOG(3,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
00602 "%.*s\n"
00603 "--end msg--",
00604 rdata->msg_info.len,
00605 pjsip_rx_data_get_info(rdata),
00606 rdata->tp_info.transport->type_name,
00607 rdata->pkt_info.src_name,
00608 rdata->pkt_info.src_port,
00609 (int)rdata->msg_info.len,
00610 rdata->msg_info.msg_buf));
00611
00612
00613 return PJ_FALSE;
00614 }
00615
00616
00617 static pj_status_t logger_on_tx_msg(pjsip_tx_data *tdata)
00618 {
00619
00620
00621
00622
00623
00624
00625
00626 PJ_LOG(3,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
00627 "%.*s\n"
00628 "--end msg--",
00629 (tdata->buf.cur - tdata->buf.start),
00630 pjsip_tx_data_get_info(tdata),
00631 tdata->tp_info.transport->type_name,
00632 tdata->tp_info.dst_name,
00633 tdata->tp_info.dst_port,
00634 (int)(tdata->buf.cur - tdata->buf.start),
00635 tdata->buf.start));
00636
00637
00638 return PJ_SUCCESS;
00639 }
00640
00641
00642 static pjsip_module msg_logger =
00643 {
00644 NULL, NULL,
00645 { "mod-siprtp-log", 14 },
00646 -1,
00647 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,
00648 NULL,
00649 NULL,
00650 NULL,
00651 NULL,
00652 &logger_on_rx_msg,
00653 &logger_on_rx_msg,
00654 &logger_on_tx_msg,
00655 &logger_on_tx_msg,
00656 NULL,
00657
00658 };
00659
00660
00661
00662
00663
00664
00665
00666 static pj_bool_t mod_test_on_rx_response(pjsip_rx_data *rdata);
00667
00668 static void call_on_media_update( pjsip_inv_session *inv,
00669 pj_status_t status);
00670 static void call_on_state_changed( pjsip_inv_session *inv,
00671 pjsip_event *e);
00672 static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e);
00673
00674
00675
00676
00677 static pjsip_module mod_test =
00678 {
00679 NULL, NULL,
00680 { "mod-test", 8 },
00681 -1,
00682 PJSIP_MOD_PRIORITY_APPLICATION,
00683 NULL,
00684 NULL,
00685 NULL,
00686 NULL,
00687 NULL,
00688 &mod_test_on_rx_response,
00689 NULL,
00690 NULL,
00691 NULL,
00692 };
00693
00694
00695 static void report_completion(int status_code)
00696 {
00697 app.client.job_finished++;
00698 if (status_code >= 200 && status_code < 800)
00699 app.client.response_codes[status_code]++;
00700 app.client.total_responses++;
00701 pj_gettimeofday(&app.client.last_completion);
00702 }
00703
00704
00705
00706 static pj_bool_t mod_test_on_rx_response(pjsip_rx_data *rdata)
00707 {
00708 if (pjsip_rdata_get_tsx(rdata) == NULL) {
00709 report_completion(rdata->msg_info.msg->line.status.code);
00710 }
00711
00712 return PJ_TRUE;
00713 }
00714
00715
00716
00717
00718
00719 static pj_status_t create_app(void)
00720 {
00721 pj_status_t status;
00722
00723 status = pj_init();
00724 if (status != PJ_SUCCESS) {
00725 app_perror(THIS_FILE, "Error initializing pjlib", status);
00726 return status;
00727 }
00728
00729
00730 status = pjlib_util_init();
00731 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00732
00733
00734 pj_caching_pool_init(&app.cp, &pj_pool_factory_default_policy,
00735 CACHING_POOL_SIZE);
00736
00737
00738 app.pool = pj_pool_create(&app.cp.factory, "app", 1000, 1000, NULL);
00739
00740
00741 status = pjsip_endpt_create(&app.cp.factory, pj_gethostname()->ptr,
00742 &app.sip_endpt);
00743 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00744
00745
00746 return status;
00747 }
00748
00749
00750
00751
00752
00753 static pj_status_t init_sip()
00754 {
00755 pj_status_t status = -1;
00756
00757
00758 {
00759 pj_sockaddr_in addr;
00760 pjsip_host_port addrname;
00761 const char *transport_type = NULL;
00762
00763 pj_bzero(&addr, sizeof(addr));
00764 addr.sin_family = pj_AF_INET();
00765 addr.sin_addr.s_addr = 0;
00766 addr.sin_port = pj_htons((pj_uint16_t)app.local_port);
00767
00768 if (app.local_addr.slen) {
00769 addrname.host = app.local_addr;
00770 addrname.port = 5060;
00771 }
00772 if (app.local_port != 0)
00773 addrname.port = app.local_port;
00774
00775 if (0) {
00776 #if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
00777 } else if (app.use_tcp) {
00778 pj_sockaddr_in local_addr;
00779 pjsip_tpfactory *tpfactory;
00780
00781 transport_type = "tcp";
00782 pj_sockaddr_in_init(&local_addr, 0, (pj_uint16_t)app.local_port);
00783 status = pjsip_tcp_transport_start(app.sip_endpt, &local_addr,
00784 app.thread_count, &tpfactory);
00785 if (status == PJ_SUCCESS) {
00786 app.local_addr = tpfactory->addr_name.host;
00787 app.local_port = tpfactory->addr_name.port;
00788 }
00789 #endif
00790 } else {
00791 pjsip_transport *tp;
00792
00793 transport_type = "udp";
00794 status = pjsip_udp_transport_start(app.sip_endpt, &addr,
00795 (app.local_addr.slen ? &addrname:NULL),
00796 app.thread_count, &tp);
00797 if (status == PJ_SUCCESS) {
00798 app.local_addr = tp->local_name.host;
00799 app.local_port = tp->local_name.port;
00800 }
00801
00802 }
00803 if (status != PJ_SUCCESS) {
00804 app_perror(THIS_FILE, "Unable to start transport", status);
00805 return status;
00806 }
00807
00808 app.local_uri.ptr = pj_pool_alloc(app.pool, 128);
00809 app.local_uri.slen = pj_ansi_sprintf(app.local_uri.ptr,
00810 "<sip:pjsip-perf@%.*s:%d;transport=%s>",
00811 (int)app.local_addr.slen,
00812 app.local_addr.ptr,
00813 app.local_port,
00814 transport_type);
00815
00816 app.local_contact = app.local_uri;
00817 }
00818
00819
00820
00821
00822
00823 status = pjsip_tsx_layer_init_module(app.sip_endpt);
00824 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00825
00826
00827 status = pjsip_ua_init_module( app.sip_endpt, NULL );
00828 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00829
00830
00831 status = pjsip_100rel_init_module(app.sip_endpt);
00832 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00833
00834
00835 {
00836 pjsip_inv_callback inv_cb;
00837
00838
00839 pj_bzero(&inv_cb, sizeof(inv_cb));
00840 inv_cb.on_state_changed = &call_on_state_changed;
00841 inv_cb.on_new_session = &call_on_forked;
00842 inv_cb.on_media_update = &call_on_media_update;
00843
00844
00845 status = pjsip_inv_usage_init(app.sip_endpt, &inv_cb);
00846 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00847 }
00848
00849
00850 status = pjsip_endpt_register_module( app.sip_endpt, &mod_test);
00851 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00852
00853
00854
00855 status = pjsip_endpt_register_module( app.sip_endpt, &mod_stateless_server);
00856 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00857
00858
00859 status = pjsip_endpt_register_module( app.sip_endpt, &mod_responder);
00860 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00861
00862
00863 status = pjsip_endpt_register_module( app.sip_endpt, &mod_stateful_server);
00864 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00865
00866
00867
00868 status = pjsip_endpt_register_module( app.sip_endpt, &mod_call_server);
00869 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00870
00871
00872
00873 return PJ_SUCCESS;
00874 }
00875
00876
00877
00878
00879
00880 static void destroy_app()
00881 {
00882 unsigned i;
00883
00884 app.thread_quit = 1;
00885 for (i=0; i<app.thread_count; ++i) {
00886 if (app.thread[i]) {
00887 pj_thread_join(app.thread[i]);
00888 pj_thread_destroy(app.thread[i]);
00889 app.thread[i] = NULL;
00890 }
00891 }
00892
00893 if (app.sip_endpt) {
00894 pjsip_endpt_destroy(app.sip_endpt);
00895 app.sip_endpt = NULL;
00896 }
00897
00898 if (app.pool) {
00899 pj_pool_release(app.pool);
00900 app.pool = NULL;
00901 PJ_LOG(3,(THIS_FILE, "Peak memory size: %uMB",
00902 app.cp.peak_used_size / 1000000));
00903 pj_caching_pool_destroy(&app.cp);
00904 }
00905
00906
00907 pj_shutdown();
00908 }
00909
00910
00911
00912
00913
00914 static pj_status_t init_media()
00915 {
00916 unsigned i;
00917 pj_uint16_t rtp_port;
00918 pj_status_t status;
00919
00920
00921
00922
00923
00924 status = pjmedia_endpt_create(&app.cp.factory,
00925 pjsip_endpt_get_ioqueue(app.sip_endpt), 0,
00926 &app.med_endpt);
00927 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00928
00929
00930
00931 pjmedia_codec_register_audio_codecs(app.med_endpt, NULL);
00932
00933
00934 app.skinfo_cnt = 0;
00935 for (i=0, rtp_port=4000; i<PJ_ARRAY_SIZE(app.skinfo); ++i, rtp_port+=2) {
00936 pjmedia_sock_info *skinfo;
00937
00938 skinfo = &app.skinfo[i];
00939
00940 pj_sockaddr_in_init(&skinfo->rtp_addr_name.ipv4, &app.local_addr,
00941 (pj_uint16_t)rtp_port);
00942 pj_sockaddr_in_init(&skinfo->rtp_addr_name.ipv4, &app.local_addr,
00943 (pj_uint16_t)(rtp_port+1));
00944 app.skinfo_cnt++;
00945 }
00946
00947
00948 dummy_sdp_str.slen = pj_ansi_strlen(dummy_sdp_str.ptr);
00949 status = pjmedia_sdp_parse(app.pool, dummy_sdp_str.ptr, dummy_sdp_str.slen,
00950 &app.dummy_sdp);
00951 if (status != PJ_SUCCESS) {
00952 app_perror(THIS_FILE, "Error parsing dummy SDP", status);
00953 return status;
00954 }
00955
00956
00957
00958 return PJ_SUCCESS;
00959 }
00960
00961
00962
00963
00964
00965 static void call_on_media_update( pjsip_inv_session *inv,
00966 pj_status_t status)
00967 {
00968 if (status != PJ_SUCCESS) {
00969 pjsip_tx_data *tdata;
00970 pj_status_t status;
00971
00972 status = pjsip_inv_end_session(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE,
00973 NULL, &tdata);
00974 if (status == PJ_SUCCESS && tdata)
00975 status = pjsip_inv_send_msg(inv, tdata);
00976 }
00977 }
00978
00979
00980
00981
00982
00983 static void call_on_state_changed( pjsip_inv_session *inv,
00984 pjsip_event *e)
00985 {
00986 PJ_UNUSED_ARG(e);
00987
00988
00989 if (inv->mod_data[mod_test.id] != NULL)
00990 return;
00991
00992
00993 if (inv->role != PJSIP_UAC_ROLE)
00994 return;
00995
00996 if (inv->state == PJSIP_INV_STATE_CONFIRMED) {
00997 pjsip_tx_data *tdata;
00998 pj_status_t status;
00999
01000
01001
01002
01003 status = pjsip_inv_end_session(inv, PJSIP_SC_OK, NULL, &tdata);
01004 if (status == PJ_SUCCESS && tdata)
01005 status = pjsip_inv_send_msg(inv, tdata);
01006
01007 } else if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
01008 report_completion(inv->cause);
01009 inv->mod_data[mod_test.id] = (void*)1;
01010 }
01011 }
01012
01013
01014
01015 static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e)
01016 {
01017
01018 PJ_UNUSED_ARG(inv);
01019 PJ_UNUSED_ARG(e);
01020 }
01021
01022
01023
01024
01025
01026 static pj_status_t make_call(const pj_str_t *dst_uri)
01027 {
01028 struct call *call;
01029 pjsip_dialog *dlg;
01030 pjmedia_sdp_session *sdp;
01031 pjsip_tx_data *tdata;
01032 pj_status_t status;
01033
01034
01035
01036 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
01037 &app.local_uri,
01038 &app.local_contact,
01039 dst_uri,
01040 dst_uri,
01041 &dlg);
01042 if (status != PJ_SUCCESS) {
01043 return status;
01044 }
01045
01046
01047 call = pj_pool_zalloc(dlg->pool, sizeof(struct call));
01048
01049
01050 if (app.real_sdp) {
01051 status = pjmedia_endpt_create_sdp(app.med_endpt, dlg->pool, 1,
01052 app.skinfo, &sdp);
01053 if (status != PJ_SUCCESS) {
01054 pjsip_dlg_terminate(dlg);
01055 return status;
01056 }
01057 } else
01058 sdp = app.dummy_sdp;
01059
01060
01061 status = pjsip_inv_create_uac( dlg, sdp, 0, &call->inv);
01062 if (status != PJ_SUCCESS) {
01063 pjsip_dlg_terminate(dlg);
01064 return status;
01065 }
01066
01067
01068
01069
01070
01071
01072 status = pjsip_inv_invite(call->inv, &tdata);
01073 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
01074
01075
01076
01077
01078
01079
01080 status = pjsip_inv_send_msg(call->inv, tdata);
01081 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
01082
01083
01084 return PJ_SUCCESS;
01085 }
01086
01087
01088
01089
01090
01091 static pj_status_t verify_sip_url(const char *c_url)
01092 {
01093 pjsip_uri *p;
01094 pj_pool_t *pool;
01095 char *url;
01096 int len = (c_url ? pj_ansi_strlen(c_url) : 0);
01097
01098 if (!len) return -1;
01099
01100 pool = pj_pool_create(&app.cp.factory, "check%p", 1024, 0, NULL);
01101 if (!pool) return PJ_ENOMEM;
01102
01103 url = pj_pool_alloc(pool, len+1);
01104 pj_ansi_strcpy(url, c_url);
01105 url[len] = '\0';
01106
01107 p = pjsip_parse_uri(pool, url, len, 0);
01108 if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0)
01109 p = NULL;
01110
01111 pj_pool_release(pool);
01112 return p ? 0 : -1;
01113 }
01114
01115
01116 static void usage(void)
01117 {
01118 printf(
01119 "Usage:\n"
01120 " pjsip-perf [OPTIONS] -- to start as server\n"
01121 " pjsip-perf [OPTIONS] URL -- to call server (possibly itself)\n"
01122 "\n"
01123 "where:\n"
01124 " URL The SIP URL to be contacted.\n"
01125 "\n"
01126 "Client options:\n"
01127 " --method=METHOD, -m Set test method (set to INVITE for call benchmark)\n"
01128 " [default: OPTIONS]\n"
01129 " --count=N, -n Set total number of requests to initiate\n"
01130 " [default=%d]\n"
01131 " --stateless, -s Set to operate in stateless mode\n"
01132 " [default: stateful]\n"
01133 " --timeout=SEC, -t Set client timeout [default=60 sec]\n"
01134 " --window=COUNT, -w Set maximum outstanding job [default: %d]\n"
01135 "\n"
01136 "SDP options (client and server):\n"
01137 " --real-sdp Generate real SDP from pjmedia, and also perform\n"
01138 " proper SDP negotiation [default: dummy]\n"
01139 "\n"
01140 "Client and Server options:\n"
01141 " --local-port=PORT, -p Set local port [default: 5060]\n"
01142 " --use-tcp, -T Use TCP instead of UDP. Note that when started as\n"
01143 " client, you must add ;transport=tcp parameter to URL\n"
01144 " [default: no]\n"
01145 " --thread-count=N Set number of worker threads [default=1]\n"
01146 " --trying Send 100/Trying response (server, default no)\n"
01147 " --ringing Send 180/Ringing response (server, default no)\n"
01148 " --delay=MS, -d Delay answering call by MS (server, default no)\n"
01149 "\n"
01150 "Misc options:\n"
01151 " --help, -h Display this screen\n"
01152 " --verbose, -v Verbose logging (put more than once for even more)\n"
01153 "\n"
01154 "When started as server, pjsip-perf can be contacted on the following URIs:\n"
01155 " - sip:0@server-addr To handle requests statelessly.\n"
01156 " - sip:1@server-addr To handle requests statefully.\n"
01157 " - sip:2@server-addr To handle INVITE call.\n",
01158 DEFAULT_COUNT, JOB_WINDOW);
01159 }
01160
01161
01162 static int my_atoi(const char *s)
01163 {
01164 pj_str_t ss = pj_str((char*)s);
01165 return pj_strtoul(&ss);
01166 }
01167
01168
01169 static pj_status_t init_options(int argc, char *argv[])
01170 {
01171 enum { OPT_THREAD_COUNT = 1, OPT_REAL_SDP, OPT_TRYING, OPT_RINGING };
01172 struct pj_getopt_option long_options[] = {
01173 { "local-port", 1, 0, 'p' },
01174 { "count", 1, 0, 'c' },
01175 { "thread-count", 1, 0, OPT_THREAD_COUNT },
01176 { "method", 1, 0, 'm' },
01177 { "help", 0, 0, 'h' },
01178 { "stateless", 0, 0, 's' },
01179 { "timeout", 1, 0, 't' },
01180 { "real-sdp", 0, 0, OPT_REAL_SDP },
01181 { "verbose", 0, 0, 'v' },
01182 { "use-tcp", 0, 0, 'T' },
01183 { "window", 1, 0, 'w' },
01184 { "delay", 1, 0, 'd' },
01185 { "trying", 0, 0, OPT_TRYING},
01186 { "ringing", 0, 0, OPT_RINGING},
01187 { NULL, 0, 0, 0 },
01188 };
01189 int c;
01190 int option_index;
01191
01192
01193 app.local_port = 5060;
01194 app.thread_count = 1;
01195 app.client.job_count = DEFAULT_COUNT;
01196 app.client.method = *pjsip_get_options_method();
01197 app.client.job_window = c = JOB_WINDOW;
01198 app.client.timeout = 60;
01199 app.log_level = 3;
01200
01201
01202
01203 pj_optind = 0;
01204 while((c=pj_getopt_long(argc,argv, "p:c:m:t:w:d:hsv",
01205 long_options, &option_index))!=-1)
01206 {
01207 switch (c) {
01208 case 'p':
01209 app.local_port = my_atoi(pj_optarg);
01210 if (app.local_port < 0 || app.local_port > 65535) {
01211 PJ_LOG(3,(THIS_FILE, "Invalid --local-port %s", pj_optarg));
01212 return -1;
01213 }
01214 break;
01215
01216 case 'c':
01217 app.client.job_count = my_atoi(pj_optarg);
01218 if (app.client.job_count < 0) {
01219 PJ_LOG(3,(THIS_FILE, "Invalid --local-port %s", pj_optarg));
01220 return -1;
01221 }
01222 if (app.client.job_count > pjsip_cfg()->tsx.max_count)
01223 PJ_LOG(3,(THIS_FILE,
01224 "Warning: --count value (%d) exceeds maximum "
01225 "transaction count (%d)", app.client.job_count,
01226 pjsip_cfg()->tsx.max_count));
01227 break;
01228
01229 case OPT_THREAD_COUNT:
01230 app.thread_count = my_atoi(pj_optarg);
01231 if (app.thread_count < 1 || app.thread_count > 16) {
01232 PJ_LOG(3,(THIS_FILE, "Invalid --thread-count %s", pj_optarg));
01233 return -1;
01234 }
01235 break;
01236
01237 case 'm':
01238 {
01239 pj_str_t temp = pj_str((char*)pj_optarg);
01240 pjsip_method_init_np(&app.client.method, &temp);
01241 }
01242 break;
01243
01244 case 'h':
01245 usage();
01246 return -1;
01247
01248 case 's':
01249 app.client.stateless = PJ_TRUE;
01250 break;
01251
01252 case OPT_REAL_SDP:
01253 app.real_sdp = 1;
01254 break;
01255
01256 case 'v':
01257 app.log_level++;
01258 break;
01259
01260 case 't':
01261 app.client.timeout = my_atoi(pj_optarg);
01262 if (app.client.timeout < 0 || app.client.timeout > 600) {
01263 PJ_LOG(3,(THIS_FILE, "Invalid --timeout %s", pj_optarg));
01264 return -1;
01265 }
01266 break;
01267
01268 case 'w':
01269 app.client.job_window = my_atoi(pj_optarg);
01270 if (app.client.job_window <= 0) {
01271 PJ_LOG(3,(THIS_FILE, "Invalid --window %s", pj_optarg));
01272 return -1;
01273 }
01274 break;
01275
01276 case 'T':
01277 app.use_tcp = PJ_TRUE;
01278 break;
01279
01280 case 'd':
01281 app.server.delay = my_atoi(pj_optarg);
01282 if (app.server.delay > 3600) {
01283 PJ_LOG(3,(THIS_FILE, "I think --delay %s is too long",
01284 pj_optarg));
01285 return -1;
01286 }
01287 break;
01288
01289 case OPT_TRYING:
01290 app.server.send_trying = 1;
01291 break;
01292
01293 case OPT_RINGING:
01294 app.server.send_ringing = 1;
01295 break;
01296
01297 default:
01298 PJ_LOG(1,(THIS_FILE,
01299 "Invalid argument. Use --help to see help"));
01300 return -1;
01301 }
01302 }
01303
01304 if (pj_optind != argc) {
01305
01306 if (verify_sip_url(argv[pj_optind]) != PJ_SUCCESS) {
01307 PJ_LOG(1,(THIS_FILE, "Invalid SIP URI %s", argv[pj_optind]));
01308 return -1;
01309 }
01310 app.client.dst_uri = pj_str(argv[pj_optind]);
01311
01312 pj_optind++;
01313
01314 }
01315
01316 if (pj_optind != argc) {
01317 PJ_LOG(1,(THIS_FILE, "Error: unknown options %s", argv[pj_optind]));
01318 return -1;
01319 }
01320
01321 return 0;
01322 }
01323
01324
01325
01326 static pj_status_t submit_stateless_job(void)
01327 {
01328 pjsip_tx_data *tdata;
01329 pj_status_t status;
01330
01331 status = pjsip_endpt_create_request(app.sip_endpt, &app.client.method,
01332 &app.client.dst_uri, &app.local_uri,
01333 &app.client.dst_uri, &app.local_contact,
01334 NULL, -1, NULL, &tdata);
01335 if (status != PJ_SUCCESS) {
01336 app_perror(THIS_FILE, "Error creating request", status);
01337 report_completion(701);
01338 return status;
01339 }
01340
01341 status = pjsip_endpt_send_request_stateless(app.sip_endpt, tdata, NULL,
01342 NULL);
01343 if (status != PJ_SUCCESS) {
01344 pjsip_tx_data_dec_ref(tdata);
01345 app_perror(THIS_FILE, "Error sending stateless request", status);
01346 report_completion(701);
01347 return status;
01348 }
01349
01350 return PJ_SUCCESS;
01351 }
01352
01353
01354
01355 static void tsx_completion_cb(void *token, pjsip_event *event)
01356 {
01357 pjsip_transaction *tsx;
01358
01359 PJ_UNUSED_ARG(token);
01360
01361 if (event->type != PJSIP_EVENT_TSX_STATE)
01362 return;
01363
01364 tsx = event->body.tsx_state.tsx;
01365
01366 if (tsx->mod_data[mod_test.id] != NULL) {
01367
01368 return;
01369 }
01370
01371 if (tsx->state==PJSIP_TSX_STATE_TERMINATED) {
01372 report_completion(tsx->status_code);
01373 tsx->mod_data[mod_test.id] = (void*)1;
01374 }
01375 else if (tsx->method.id == PJSIP_INVITE_METHOD &&
01376 tsx->state == PJSIP_TSX_STATE_CONFIRMED) {
01377
01378 report_completion(tsx->status_code);
01379 tsx->mod_data[mod_test.id] = (void*)1;
01380
01381 } else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
01382
01383 report_completion(tsx->status_code);
01384 tsx->mod_data[mod_test.id] = (void*)1;
01385
01386 TERMINATE_TSX(tsx, tsx->status_code);
01387 }
01388 }
01389
01390
01391
01392 static pj_status_t submit_job(void)
01393 {
01394 pjsip_tx_data *tdata;
01395 pj_status_t status;
01396
01397 status = pjsip_endpt_create_request(app.sip_endpt, &app.client.method,
01398 &app.client.dst_uri, &app.local_uri,
01399 &app.client.dst_uri, &app.local_contact,
01400 NULL, -1, NULL, &tdata);
01401 if (status != PJ_SUCCESS) {
01402 app_perror(THIS_FILE, "Error creating request", status);
01403 report_completion(701);
01404 return status;
01405 }
01406
01407 status = pjsip_endpt_send_request(app.sip_endpt, tdata, -1, NULL,
01408 &tsx_completion_cb);
01409 if (status != PJ_SUCCESS) {
01410 app_perror(THIS_FILE, "Error sending stateful request", status);
01411
01412
01413
01414
01415 }
01416 return status;
01417 }
01418
01419
01420
01421 static int client_thread(void *arg)
01422 {
01423 pj_time_val end_time, last_report, now;
01424 unsigned thread_index = (unsigned)(long)arg;
01425 unsigned cycle = 0, last_cycle = 0;
01426
01427 pj_thread_sleep(100);
01428
01429 pj_gettimeofday(&end_time);
01430 end_time.sec += app.client.timeout;
01431
01432 pj_gettimeofday(&last_report);
01433
01434 if (app.client.first_request.sec == 0) {
01435 pj_gettimeofday(&app.client.first_request);
01436 }
01437
01438
01439 while (app.client.job_submitted < app.client.job_count && !app.thread_quit){
01440 pj_time_val timeout = { 0, 1 };
01441 unsigned i;
01442 int outstanding;
01443 pj_status_t status;
01444
01445
01446 outstanding = app.client.job_submitted - app.client.job_finished;
01447
01448
01449 if (outstanding > (int)app.client.stat_max_window)
01450 app.client.stat_max_window = outstanding;
01451
01452
01453
01454
01455
01456 for (i=0; outstanding > (int)app.client.job_window && i<1000; ++i) {
01457 pj_time_val wait = { 0, 500 };
01458 unsigned count = 0;
01459
01460 pjsip_endpt_handle_events2(app.sip_endpt, &wait, &count);
01461 outstanding = app.client.job_submitted - app.client.job_finished;
01462
01463 if (count == 0)
01464 break;
01465
01466 ++cycle;
01467 }
01468
01469
01470
01471 if (app.client.method.id == PJSIP_INVITE_METHOD) {
01472 status = make_call(&app.client.dst_uri);
01473 } else if (app.client.stateless) {
01474 status = submit_stateless_job();
01475 } else {
01476 status = submit_job();
01477 }
01478
01479 ++app.client.job_submitted;
01480 ++cycle;
01481
01482
01483 pjsip_endpt_handle_events2(app.sip_endpt, &timeout, NULL);
01484
01485
01486 if (cycle - last_cycle >= 500) {
01487 pj_gettimeofday(&now);
01488 if (PJ_TIME_VAL_GTE(now, end_time)) {
01489 break;
01490 }
01491 last_cycle = cycle;
01492
01493
01494 if (thread_index == 0 && now.sec-last_report.sec >= 2) {
01495 printf("\r%d jobs started, %d completed... ",
01496 app.client.job_submitted, app.client.job_finished);
01497 fflush(stdout);
01498 last_report = now;
01499 }
01500 }
01501 }
01502
01503 if (app.client.requests_sent.sec == 0) {
01504 pj_gettimeofday(&app.client.requests_sent);
01505 }
01506
01507
01508 if (thread_index == 0) {
01509 printf("\r%d jobs started, %d completed%s\n",
01510 app.client.job_submitted, app.client.job_finished,
01511 (app.client.job_submitted!=app.client.job_finished ?
01512 ", waiting..." : ".") );
01513 fflush(stdout);
01514 }
01515
01516
01517 pj_gettimeofday(&now);
01518 while (PJ_TIME_VAL_LT(now, end_time) &&
01519 app.client.job_finished < app.client.job_count &&
01520 !app.thread_quit)
01521 {
01522 pj_time_val timeout = { 0, 1 };
01523 unsigned i;
01524
01525 for (i=0; i<1000; ++i) {
01526 unsigned count;
01527 count = 0;
01528 pjsip_endpt_handle_events2(app.sip_endpt, &timeout, &count);
01529 if (count == 0)
01530 break;
01531 }
01532
01533 pj_gettimeofday(&now);
01534 }
01535
01536
01537 pj_gettimeofday(&now);
01538 end_time = now;
01539 end_time.sec += 2;
01540 while (PJ_TIME_VAL_LT(now, end_time))
01541 {
01542 pj_time_val timeout = { 0, 1 };
01543 unsigned i;
01544
01545 for (i=0; i<1000; ++i) {
01546 unsigned count;
01547 count = 0;
01548 pjsip_endpt_handle_events2(app.sip_endpt, &timeout, &count);
01549 if (count == 0)
01550 break;
01551 }
01552
01553 pj_gettimeofday(&now);
01554 }
01555
01556 return 0;
01557 }
01558
01559
01560 static const char *good_number(char *buf, pj_int32_t val)
01561 {
01562 if (val < 1000) {
01563 pj_ansi_sprintf(buf, "%d", val);
01564 } else if (val < 1000000) {
01565 pj_ansi_sprintf(buf, "%d.%dK",
01566 val / 1000,
01567 (val % 1000) / 100);
01568 } else {
01569 pj_ansi_sprintf(buf, "%d.%02dM",
01570 val / 1000000,
01571 (val % 1000000) / 10000);
01572 }
01573
01574 return buf;
01575 }
01576
01577
01578 static int server_thread(void *arg)
01579 {
01580 pj_time_val timeout = { 0, 1 };
01581 unsigned thread_index = (unsigned)(long)arg;
01582 pj_time_val last_report, next_report;
01583
01584 pj_gettimeofday(&last_report);
01585 next_report = last_report;
01586 next_report.sec++;
01587
01588 while (!app.thread_quit) {
01589 pj_time_val now;
01590 unsigned i;
01591
01592 for (i=0; i<100; ++i) {
01593 unsigned count = 0;
01594 pjsip_endpt_handle_events2(app.sip_endpt, &timeout, &count);
01595 if (count == 0)
01596 break;
01597 }
01598
01599 if (thread_index == 0) {
01600 pj_gettimeofday(&now);
01601
01602 if (PJ_TIME_VAL_GTE(now, next_report)) {
01603 pj_time_val tmp;
01604 unsigned msec;
01605 unsigned stateless, stateful, call;
01606 char str_stateless[32], str_stateful[32], str_call[32];
01607
01608 tmp = now;
01609 PJ_TIME_VAL_SUB(tmp, last_report);
01610 msec = PJ_TIME_VAL_MSEC(tmp);
01611
01612 last_report = now;
01613 next_report = last_report;
01614 next_report.sec++;
01615
01616 stateless = app.server.cur_state.stateless_cnt - app.server.prev_state.stateless_cnt;
01617 stateful = app.server.cur_state.stateful_cnt - app.server.prev_state.stateful_cnt;
01618 call = app.server.cur_state.call_cnt - app.server.prev_state.call_cnt;
01619
01620 good_number(str_stateless, app.server.cur_state.stateless_cnt);
01621 good_number(str_stateful, app.server.cur_state.stateful_cnt);
01622 good_number(str_call, app.server.cur_state.call_cnt);
01623
01624 printf("Total(rate): stateless:%s (%d/s), statefull:%s (%d/s), call:%s (%d/s) \r",
01625 str_stateless, stateless*1000/msec,
01626 str_stateful, stateful*1000/msec,
01627 str_call, call*1000/msec);
01628 fflush(stdout);
01629
01630 app.server.prev_state = app.server.cur_state;
01631 }
01632 }
01633 }
01634
01635 return 0;
01636 }
01637
01638 static void write_report(const char *msg)
01639 {
01640 puts(msg);
01641
01642 #if defined(PJ_WIN32) && PJ_WIN32!=0
01643 OutputDebugString(msg);
01644 OutputDebugString("\n");
01645 #endif
01646 }
01647
01648
01649 int main(int argc, char *argv[])
01650 {
01651 static char report[1024];
01652
01653 printf("PJSIP Performance Measurement Tool v%s\n"
01654 "(c)2006 pjsip.org\n\n",
01655 PJ_VERSION);
01656
01657 if (create_app() != 0)
01658 return 1;
01659
01660 if (init_options(argc, argv) != 0)
01661 return 1;
01662
01663 if (init_sip() != 0)
01664 return 1;
01665
01666 if (init_media() != 0)
01667 return 1;
01668
01669 pj_log_set_level(app.log_level);
01670
01671 if (app.log_level > 4) {
01672 pjsip_endpt_register_module(app.sip_endpt, &msg_logger);
01673 }
01674
01675
01676
01677 if (app.client.dst_uri.slen != 0) {
01678 if (app.client.method.id == PJSIP_INVITE_METHOD) {
01679 if (app.client.stateless) {
01680 PJ_LOG(3,(THIS_FILE,
01681 "Info: --stateless option makes no sense for INVITE,"
01682 " ignored."));
01683 }
01684 }
01685
01686 }
01687
01688
01689
01690 if (app.client.dst_uri.slen) {
01691
01692 pj_status_t status;
01693 char test_type[64];
01694 unsigned msec_req, msec_res;
01695 unsigned i;
01696
01697
01698 if (app.client.method.id == PJSIP_INVITE_METHOD) {
01699 pj_ansi_strcpy(test_type, "INVITE calls");
01700 } else if (app.client.stateless) {
01701 pj_ansi_sprintf(test_type, "stateless %.*s requests",
01702 (int)app.client.method.name.slen,
01703 app.client.method.name.ptr);
01704 } else {
01705 pj_ansi_sprintf(test_type, "stateful %.*s requests",
01706 (int)app.client.method.name.slen,
01707 app.client.method.name.ptr);
01708 }
01709
01710
01711 printf("Sending %d %s to '%.*s' with %d maximum outstanding jobs, please wait..\n",
01712 app.client.job_count, test_type,
01713 (int)app.client.dst_uri.slen, app.client.dst_uri.ptr,
01714 app.client.job_window);
01715
01716 for (i=0; i<app.thread_count; ++i) {
01717 status = pj_thread_create(app.pool, NULL, &client_thread,
01718 (void*)(long)i, 0, 0, &app.thread[i]);
01719 if (status != PJ_SUCCESS) {
01720 app_perror(THIS_FILE, "Unable to create thread", status);
01721 return 1;
01722 }
01723 }
01724
01725 for (i=0; i<app.thread_count; ++i) {
01726 pj_thread_join(app.thread[i]);
01727 app.thread[i] = NULL;
01728 }
01729
01730 if (app.client.last_completion.sec) {
01731 pj_time_val duration;
01732 duration = app.client.last_completion;
01733 PJ_TIME_VAL_SUB(duration, app.client.first_request);
01734 msec_res = PJ_TIME_VAL_MSEC(duration);
01735 } else {
01736 msec_res = app.client.timeout * 1000;
01737 }
01738
01739 if (msec_res == 0) msec_res = 1;
01740
01741 if (app.client.requests_sent.sec) {
01742 pj_time_val duration;
01743 duration = app.client.requests_sent;
01744 PJ_TIME_VAL_SUB(duration, app.client.first_request);
01745 msec_req = PJ_TIME_VAL_MSEC(duration);
01746 } else {
01747 msec_req = app.client.timeout * 1000;
01748 }
01749
01750 if (msec_req == 0) msec_req = 1;
01751
01752 if (app.client.job_submitted < app.client.job_count)
01753 puts("\ntimed-out!\n");
01754 else
01755 puts("\ndone.\n");
01756
01757 pj_ansi_snprintf(
01758 report, sizeof(report),
01759 "Total %d %s sent in %d ms at rate of %d/sec\n"
01760 "Total %d responses receieved in %d ms at rate of %d/sec:",
01761 app.client.job_submitted, test_type, msec_req,
01762 app.client.job_submitted * 1000 / msec_req,
01763 app.client.total_responses, msec_res,
01764 app.client.total_responses*1000/msec_res);
01765 write_report(report);
01766
01767
01768 pj_ansi_sprintf(report, "\nDetailed responses received:");
01769 write_report(report);
01770
01771 for (i=0; i<PJ_ARRAY_SIZE(app.client.response_codes); ++i) {
01772 const pj_str_t *reason;
01773
01774 if (app.client.response_codes[i] == 0)
01775 continue;
01776
01777 reason = pjsip_get_status_text(i);
01778 pj_ansi_snprintf( report, sizeof(report),
01779 " - %d responses: %7d (%.*s)",
01780 i, app.client.response_codes[i],
01781 (int)reason->slen, reason->ptr);
01782 write_report(report);
01783 }
01784
01785
01786 pj_ansi_snprintf( report, sizeof(report),
01787 " ------\n"
01788 " TOTAL responses: %7d (rate=%d/sec)\n",
01789 app.client.total_responses,
01790 app.client.total_responses*1000/msec_res);
01791
01792 write_report(report);
01793
01794 pj_ansi_sprintf(report, "Maximum outstanding job: %d",
01795 app.client.stat_max_window);
01796 write_report(report);
01797
01798
01799 } else {
01800
01801 char s[10], *unused;
01802 pj_status_t status;
01803 unsigned i;
01804
01805 puts("pjsip-perf started in server-mode");
01806
01807 printf("Receiving requests on the following URIs:\n"
01808 " sip:0@%.*s:%d%s for stateless handling\n"
01809 " sip:1@%.*s:%d%s for stateful handling\n"
01810 " sip:2@%.*s:%d%s for call handling\n",
01811 (int)app.local_addr.slen,
01812 app.local_addr.ptr,
01813 app.local_port,
01814 (app.use_tcp ? ";transport=tcp" : ""),
01815 (int)app.local_addr.slen,
01816 app.local_addr.ptr,
01817 app.local_port,
01818 (app.use_tcp ? ";transport=tcp" : ""),
01819 (int)app.local_addr.slen,
01820 app.local_addr.ptr,
01821 app.local_port,
01822 (app.use_tcp ? ";transport=tcp" : ""));
01823 printf("INVITE with non-matching user part will be handled call-statefully\n");
01824
01825 for (i=0; i<app.thread_count; ++i) {
01826 status = pj_thread_create(app.pool, NULL, &server_thread,
01827 (void*)(long)i, 0, 0, &app.thread[i]);
01828 if (status != PJ_SUCCESS) {
01829 app_perror(THIS_FILE, "Unable to create thread", status);
01830 return 1;
01831 }
01832 }
01833
01834 puts("\nPress <ENTER> to quit\n");
01835 fflush(stdout);
01836 unused = fgets(s, sizeof(s), stdin);
01837 PJ_UNUSED_ARG(unused);
01838
01839 app.thread_quit = PJ_TRUE;
01840 for (i=0; i<app.thread_count; ++i) {
01841 pj_thread_join(app.thread[i]);
01842 app.thread[i] = NULL;
01843 }
01844
01845 puts("");
01846 }
01847
01848
01849 destroy_app();
01850
01851 return 0;
01852 }
01853
PJMEDIA small footprint Open Source media stack
Copyright (C) 2006-2008 Teluu Inc.
|