|
HOME
SIP/media Features
High Performance SIP
Small Footprint SIP
Symbian Port
FAQ
Documentation
Licensing
Download
Development (Trac)
Projects using pjsip
Mailing List
Open Source Links
About: PJLIB, PJLIB-UTIL, PJSIP, and PJMEDIA are created by: Benny Prijono <bennylp pjsip.org>
|
|
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 #if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0
00932 pjmedia_codec_g711_init(app.med_endpt);
00933 #endif
00934 #if defined(PJMEDIA_HAS_GSM_CODEC) && PJMEDIA_HAS_GSM_CODEC!=0
00935 pjmedia_codec_gsm_init(app.med_endpt);
00936 #endif
00937 #if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0
00938 pjmedia_codec_speex_init(app.med_endpt, PJMEDIA_SPEEX_NO_UWB, 3, 3);
00939 #endif
00940 #if defined(PJMEDIA_HAS_G722_CODEC) && PJMEDIA_HAS_G722_CODEC!=0
00941 pjmedia_codec_g722_init(app.med_endpt);
00942 #endif
00943
00944
00945 app.skinfo_cnt = 0;
00946 for (i=0, rtp_port=4000; i<PJ_ARRAY_SIZE(app.skinfo); ++i, rtp_port+=2) {
00947 pjmedia_sock_info *skinfo;
00948
00949 skinfo = &app.skinfo[i];
00950
00951 pj_sockaddr_in_init(&skinfo->rtp_addr_name.ipv4, &app.local_addr,
00952 (pj_uint16_t)rtp_port);
00953 pj_sockaddr_in_init(&skinfo->rtp_addr_name.ipv4, &app.local_addr,
00954 (pj_uint16_t)(rtp_port+1));
00955 app.skinfo_cnt++;
00956 }
00957
00958
00959 dummy_sdp_str.slen = pj_ansi_strlen(dummy_sdp_str.ptr);
00960 status = pjmedia_sdp_parse(app.pool, dummy_sdp_str.ptr, dummy_sdp_str.slen,
00961 &app.dummy_sdp);
00962 if (status != PJ_SUCCESS) {
00963 app_perror(THIS_FILE, "Error parsing dummy SDP", status);
00964 return status;
00965 }
00966
00967
00968
00969 return PJ_SUCCESS;
00970 }
00971
00972
00973
00974
00975
00976 static void call_on_media_update( pjsip_inv_session *inv,
00977 pj_status_t status)
00978 {
00979 if (status != PJ_SUCCESS) {
00980 pjsip_tx_data *tdata;
00981 pj_status_t status;
00982
00983 status = pjsip_inv_end_session(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE,
00984 NULL, &tdata);
00985 if (status == PJ_SUCCESS && tdata)
00986 status = pjsip_inv_send_msg(inv, tdata);
00987 }
00988 }
00989
00990
00991
00992
00993
00994 static void call_on_state_changed( pjsip_inv_session *inv,
00995 pjsip_event *e)
00996 {
00997 PJ_UNUSED_ARG(e);
00998
00999
01000 if (inv->mod_data[mod_test.id] != NULL)
01001 return;
01002
01003
01004 if (inv->role != PJSIP_UAC_ROLE)
01005 return;
01006
01007 if (inv->state == PJSIP_INV_STATE_CONFIRMED) {
01008 pjsip_tx_data *tdata;
01009 pj_status_t status;
01010
01011
01012
01013
01014 status = pjsip_inv_end_session(inv, PJSIP_SC_OK, NULL, &tdata);
01015 if (status == PJ_SUCCESS && tdata)
01016 status = pjsip_inv_send_msg(inv, tdata);
01017
01018 } else if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
01019 report_completion(inv->cause);
01020 inv->mod_data[mod_test.id] = (void*)1;
01021 }
01022 }
01023
01024
01025
01026 static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e)
01027 {
01028
01029 PJ_UNUSED_ARG(inv);
01030 PJ_UNUSED_ARG(e);
01031 }
01032
01033
01034
01035
01036
01037 static pj_status_t make_call(const pj_str_t *dst_uri)
01038 {
01039 struct call *call;
01040 pjsip_dialog *dlg;
01041 pjmedia_sdp_session *sdp;
01042 pjsip_tx_data *tdata;
01043 pj_status_t status;
01044
01045
01046
01047 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
01048 &app.local_uri,
01049 &app.local_contact,
01050 dst_uri,
01051 dst_uri,
01052 &dlg);
01053 if (status != PJ_SUCCESS) {
01054 return status;
01055 }
01056
01057
01058 call = pj_pool_zalloc(dlg->pool, sizeof(struct call));
01059
01060
01061 if (app.real_sdp) {
01062 status = pjmedia_endpt_create_sdp(app.med_endpt, dlg->pool, 1,
01063 app.skinfo, &sdp);
01064 if (status != PJ_SUCCESS) {
01065 pjsip_dlg_terminate(dlg);
01066 return status;
01067 }
01068 } else
01069 sdp = app.dummy_sdp;
01070
01071
01072 status = pjsip_inv_create_uac( dlg, sdp, 0, &call->inv);
01073 if (status != PJ_SUCCESS) {
01074 pjsip_dlg_terminate(dlg);
01075 return status;
01076 }
01077
01078
01079
01080
01081
01082
01083 status = pjsip_inv_invite(call->inv, &tdata);
01084 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
01085
01086
01087
01088
01089
01090
01091 status = pjsip_inv_send_msg(call->inv, tdata);
01092 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
01093
01094
01095 return PJ_SUCCESS;
01096 }
01097
01098
01099
01100
01101
01102 static pj_status_t verify_sip_url(const char *c_url)
01103 {
01104 pjsip_uri *p;
01105 pj_pool_t *pool;
01106 char *url;
01107 int len = (c_url ? pj_ansi_strlen(c_url) : 0);
01108
01109 if (!len) return -1;
01110
01111 pool = pj_pool_create(&app.cp.factory, "check%p", 1024, 0, NULL);
01112 if (!pool) return PJ_ENOMEM;
01113
01114 url = pj_pool_alloc(pool, len+1);
01115 pj_ansi_strcpy(url, c_url);
01116 url[len] = '\0';
01117
01118 p = pjsip_parse_uri(pool, url, len, 0);
01119 if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0)
01120 p = NULL;
01121
01122 pj_pool_release(pool);
01123 return p ? 0 : -1;
01124 }
01125
01126
01127 static void usage(void)
01128 {
01129 printf(
01130 "Usage:\n"
01131 " pjsip-perf [OPTIONS] -- to start as server\n"
01132 " pjsip-perf [OPTIONS] URL -- to call server (possibly itself)\n"
01133 "\n"
01134 "where:\n"
01135 " URL The SIP URL to be contacted.\n"
01136 "\n"
01137 "Client options:\n"
01138 " --method=METHOD, -m Set test method (set to INVITE for call benchmark)\n"
01139 " [default: OPTIONS]\n"
01140 " --count=N, -n Set total number of requests to initiate\n"
01141 " [default=%d]\n"
01142 " --stateless, -s Set to operate in stateless mode\n"
01143 " [default: stateful]\n"
01144 " --timeout=SEC, -t Set client timeout [default=60 sec]\n"
01145 " --window=COUNT, -w Set maximum outstanding job [default: %d]\n"
01146 "\n"
01147 "SDP options (client and server):\n"
01148 " --real-sdp Generate real SDP from pjmedia, and also perform\n"
01149 " proper SDP negotiation [default: dummy]\n"
01150 "\n"
01151 "Client and Server options:\n"
01152 " --local-port=PORT, -p Set local port [default: 5060]\n"
01153 " --use-tcp, -T Use TCP instead of UDP. Note that when started as\n"
01154 " client, you must add ;transport=tcp parameter to URL\n"
01155 " [default: no]\n"
01156 " --thread-count=N Set number of worker threads [default=1]\n"
01157 " --trying Send 100/Trying response (server, default no)\n"
01158 " --ringing Send 180/Ringing response (server, default no)\n"
01159 " --delay=MS, -d Delay answering call by MS (server, default no)\n"
01160 "\n"
01161 "Misc options:\n"
01162 " --help, -h Display this screen\n"
01163 " --verbose, -v Verbose logging (put more than once for even more)\n"
01164 "\n"
01165 "When started as server, pjsip-perf can be contacted on the following URIs:\n"
01166 " - sip:0@server-addr To handle requests statelessly.\n"
01167 " - sip:1@server-addr To handle requests statefully.\n"
01168 " - sip:2@server-addr To handle INVITE call.\n",
01169 DEFAULT_COUNT, JOB_WINDOW);
01170 }
01171
01172
01173 static int my_atoi(const char *s)
01174 {
01175 pj_str_t ss = pj_str((char*)s);
01176 return pj_strtoul(&ss);
01177 }
01178
01179
01180 static pj_status_t init_options(int argc, char *argv[])
01181 {
01182 enum { OPT_THREAD_COUNT = 1, OPT_REAL_SDP, OPT_TRYING, OPT_RINGING };
01183 struct pj_getopt_option long_options[] = {
01184 { "local-port", 1, 0, 'p' },
01185 { "count", 1, 0, 'c' },
01186 { "thread-count", 1, 0, OPT_THREAD_COUNT },
01187 { "method", 1, 0, 'm' },
01188 { "help", 0, 0, 'h' },
01189 { "stateless", 0, 0, 's' },
01190 { "timeout", 1, 0, 't' },
01191 { "real-sdp", 0, 0, OPT_REAL_SDP },
01192 { "verbose", 0, 0, 'v' },
01193 { "use-tcp", 0, 0, 'T' },
01194 { "window", 1, 0, 'w' },
01195 { "delay", 1, 0, 'd' },
01196 { "trying", 0, 0, OPT_TRYING},
01197 { "ringing", 0, 0, OPT_RINGING},
01198 { NULL, 0, 0, 0 },
01199 };
01200 int c;
01201 int option_index;
01202
01203
01204 app.local_port = 5060;
01205 app.thread_count = 1;
01206 app.client.job_count = DEFAULT_COUNT;
01207 app.client.method = *pjsip_get_options_method();
01208 app.client.job_window = c = JOB_WINDOW;
01209 app.client.timeout = 60;
01210 app.log_level = 3;
01211
01212
01213
01214 pj_optind = 0;
01215 while((c=pj_getopt_long(argc,argv, "p:c:m:t:w:d:hsv",
01216 long_options, &option_index))!=-1)
01217 {
01218 switch (c) {
01219 case 'p':
01220 app.local_port = my_atoi(pj_optarg);
01221 if (app.local_port < 0 || app.local_port > 65535) {
01222 PJ_LOG(3,(THIS_FILE, "Invalid --local-port %s", pj_optarg));
01223 return -1;
01224 }
01225 break;
01226
01227 case 'c':
01228 app.client.job_count = my_atoi(pj_optarg);
01229 if (app.client.job_count < 0) {
01230 PJ_LOG(3,(THIS_FILE, "Invalid --local-port %s", pj_optarg));
01231 return -1;
01232 }
01233 if (app.client.job_count > pjsip_cfg()->tsx.max_count)
01234 PJ_LOG(3,(THIS_FILE,
01235 "Warning: --count value (%d) exceeds maximum "
01236 "transaction count (%d)", app.client.job_count,
01237 pjsip_cfg()->tsx.max_count));
01238 break;
01239
01240 case OPT_THREAD_COUNT:
01241 app.thread_count = my_atoi(pj_optarg);
01242 if (app.thread_count < 1 || app.thread_count > 16) {
01243 PJ_LOG(3,(THIS_FILE, "Invalid --thread-count %s", pj_optarg));
01244 return -1;
01245 }
01246 break;
01247
01248 case 'm':
01249 {
01250 pj_str_t temp = pj_str((char*)pj_optarg);
01251 pjsip_method_init_np(&app.client.method, &temp);
01252 }
01253 break;
01254
01255 case 'h':
01256 usage();
01257 return -1;
01258
01259 case 's':
01260 app.client.stateless = PJ_TRUE;
01261 break;
01262
01263 case OPT_REAL_SDP:
01264 app.real_sdp = 1;
01265 break;
01266
01267 case 'v':
01268 app.log_level++;
01269 break;
01270
01271 case 't':
01272 app.client.timeout = my_atoi(pj_optarg);
01273 if (app.client.timeout < 0 || app.client.timeout > 600) {
01274 PJ_LOG(3,(THIS_FILE, "Invalid --timeout %s", pj_optarg));
01275 return -1;
01276 }
01277 break;
01278
01279 case 'w':
01280 app.client.job_window = my_atoi(pj_optarg);
01281 if (app.client.job_window <= 0) {
01282 PJ_LOG(3,(THIS_FILE, "Invalid --window %s", pj_optarg));
01283 return -1;
01284 }
01285 break;
01286
01287 case 'T':
01288 app.use_tcp = PJ_TRUE;
01289 break;
01290
01291 case 'd':
01292 app.server.delay = my_atoi(pj_optarg);
01293 if (app.server.delay > 3600) {
01294 PJ_LOG(3,(THIS_FILE, "I think --delay %s is too long",
01295 pj_optarg));
01296 return -1;
01297 }
01298 break;
01299
01300 case OPT_TRYING:
01301 app.server.send_trying = 1;
01302 break;
01303
01304 case OPT_RINGING:
01305 app.server.send_ringing = 1;
01306 break;
01307
01308 default:
01309 PJ_LOG(1,(THIS_FILE,
01310 "Invalid argument. Use --help to see help"));
01311 return -1;
01312 }
01313 }
01314
01315 if (pj_optind != argc) {
01316
01317 if (verify_sip_url(argv[pj_optind]) != PJ_SUCCESS) {
01318 PJ_LOG(1,(THIS_FILE, "Invalid SIP URI %s", argv[pj_optind]));
01319 return -1;
01320 }
01321 app.client.dst_uri = pj_str(argv[pj_optind]);
01322
01323 pj_optind++;
01324
01325 }
01326
01327 if (pj_optind != argc) {
01328 PJ_LOG(1,(THIS_FILE, "Error: unknown options %s", argv[pj_optind]));
01329 return -1;
01330 }
01331
01332 return 0;
01333 }
01334
01335
01336
01337 static pj_status_t submit_stateless_job(void)
01338 {
01339 pjsip_tx_data *tdata;
01340 pj_status_t status;
01341
01342 status = pjsip_endpt_create_request(app.sip_endpt, &app.client.method,
01343 &app.client.dst_uri, &app.local_uri,
01344 &app.client.dst_uri, &app.local_contact,
01345 NULL, -1, NULL, &tdata);
01346 if (status != PJ_SUCCESS) {
01347 app_perror(THIS_FILE, "Error creating request", status);
01348 report_completion(701);
01349 return status;
01350 }
01351
01352 status = pjsip_endpt_send_request_stateless(app.sip_endpt, tdata, NULL,
01353 NULL);
01354 if (status != PJ_SUCCESS) {
01355 pjsip_tx_data_dec_ref(tdata);
01356 app_perror(THIS_FILE, "Error sending stateless request", status);
01357 report_completion(701);
01358 return status;
01359 }
01360
01361 return PJ_SUCCESS;
01362 }
01363
01364
01365
01366 static void tsx_completion_cb(void *token, pjsip_event *event)
01367 {
01368 pjsip_transaction *tsx;
01369
01370 PJ_UNUSED_ARG(token);
01371
01372 if (event->type != PJSIP_EVENT_TSX_STATE)
01373 return;
01374
01375 tsx = event->body.tsx_state.tsx;
01376
01377 if (tsx->mod_data[mod_test.id] != NULL) {
01378
01379 return;
01380 }
01381
01382 if (tsx->state==PJSIP_TSX_STATE_TERMINATED) {
01383 report_completion(tsx->status_code);
01384 tsx->mod_data[mod_test.id] = (void*)1;
01385 }
01386 else if (tsx->method.id == PJSIP_INVITE_METHOD &&
01387 tsx->state == PJSIP_TSX_STATE_CONFIRMED) {
01388
01389 report_completion(tsx->status_code);
01390 tsx->mod_data[mod_test.id] = (void*)1;
01391
01392 } else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
01393
01394 report_completion(tsx->status_code);
01395 tsx->mod_data[mod_test.id] = (void*)1;
01396
01397 TERMINATE_TSX(tsx, tsx->status_code);
01398 }
01399 }
01400
01401
01402
01403 static pj_status_t submit_job(void)
01404 {
01405 pjsip_tx_data *tdata;
01406 pj_status_t status;
01407
01408 status = pjsip_endpt_create_request(app.sip_endpt, &app.client.method,
01409 &app.client.dst_uri, &app.local_uri,
01410 &app.client.dst_uri, &app.local_contact,
01411 NULL, -1, NULL, &tdata);
01412 if (status != PJ_SUCCESS) {
01413 app_perror(THIS_FILE, "Error creating request", status);
01414 report_completion(701);
01415 return status;
01416 }
01417
01418 status = pjsip_endpt_send_request(app.sip_endpt, tdata, -1, NULL,
01419 &tsx_completion_cb);
01420 if (status != PJ_SUCCESS) {
01421 app_perror(THIS_FILE, "Error sending stateful request", status);
01422
01423
01424
01425
01426 }
01427 return status;
01428 }
01429
01430
01431
01432 static int client_thread(void *arg)
01433 {
01434 pj_time_val end_time, last_report, now;
01435 unsigned thread_index = (unsigned)(long)arg;
01436 unsigned cycle = 0, last_cycle = 0;
01437
01438 pj_thread_sleep(100);
01439
01440 pj_gettimeofday(&end_time);
01441 end_time.sec += app.client.timeout;
01442
01443 pj_gettimeofday(&last_report);
01444
01445 if (app.client.first_request.sec == 0) {
01446 pj_gettimeofday(&app.client.first_request);
01447 }
01448
01449
01450 while (app.client.job_submitted < app.client.job_count && !app.thread_quit){
01451 pj_time_val timeout = { 0, 1 };
01452 unsigned i;
01453 int outstanding;
01454 pj_status_t status;
01455
01456
01457 outstanding = app.client.job_submitted - app.client.job_finished;
01458
01459
01460 if (outstanding > (int)app.client.stat_max_window)
01461 app.client.stat_max_window = outstanding;
01462
01463
01464
01465
01466
01467 for (i=0; outstanding > (int)app.client.job_window && i<1000; ++i) {
01468 pj_time_val wait = { 0, 500 };
01469 unsigned count = 0;
01470
01471 pjsip_endpt_handle_events2(app.sip_endpt, &wait, &count);
01472 outstanding = app.client.job_submitted - app.client.job_finished;
01473
01474 if (count == 0)
01475 break;
01476
01477 ++cycle;
01478 }
01479
01480
01481
01482 if (app.client.method.id == PJSIP_INVITE_METHOD) {
01483 status = make_call(&app.client.dst_uri);
01484 } else if (app.client.stateless) {
01485 status = submit_stateless_job();
01486 } else {
01487 status = submit_job();
01488 }
01489
01490 ++app.client.job_submitted;
01491 ++cycle;
01492
01493
01494 pjsip_endpt_handle_events2(app.sip_endpt, &timeout, NULL);
01495
01496
01497 if (cycle - last_cycle >= 500) {
01498 pj_gettimeofday(&now);
01499 if (PJ_TIME_VAL_GTE(now, end_time)) {
01500 break;
01501 }
01502 last_cycle = cycle;
01503
01504
01505 if (thread_index == 0 && now.sec-last_report.sec >= 2) {
01506 printf("\r%d jobs started, %d completed... ",
01507 app.client.job_submitted, app.client.job_finished);
01508 fflush(stdout);
01509 last_report = now;
01510 }
01511 }
01512 }
01513
01514 if (app.client.requests_sent.sec == 0) {
01515 pj_gettimeofday(&app.client.requests_sent);
01516 }
01517
01518
01519 if (thread_index == 0) {
01520 printf("\r%d jobs started, %d completed%s\n",
01521 app.client.job_submitted, app.client.job_finished,
01522 (app.client.job_submitted!=app.client.job_finished ?
01523 ", waiting..." : ".") );
01524 fflush(stdout);
01525 }
01526
01527
01528 pj_gettimeofday(&now);
01529 while (PJ_TIME_VAL_LT(now, end_time) &&
01530 app.client.job_finished < app.client.job_count &&
01531 !app.thread_quit)
01532 {
01533 pj_time_val timeout = { 0, 1 };
01534 unsigned i;
01535
01536 for (i=0; i<1000; ++i) {
01537 unsigned count;
01538 count = 0;
01539 pjsip_endpt_handle_events2(app.sip_endpt, &timeout, &count);
01540 if (count == 0)
01541 break;
01542 }
01543
01544 pj_gettimeofday(&now);
01545 }
01546
01547
01548 pj_gettimeofday(&now);
01549 end_time = now;
01550 end_time.sec += 2;
01551 while (PJ_TIME_VAL_LT(now, end_time))
01552 {
01553 pj_time_val timeout = { 0, 1 };
01554 unsigned i;
01555
01556 for (i=0; i<1000; ++i) {
01557 unsigned count;
01558 count = 0;
01559 pjsip_endpt_handle_events2(app.sip_endpt, &timeout, &count);
01560 if (count == 0)
01561 break;
01562 }
01563
01564 pj_gettimeofday(&now);
01565 }
01566
01567 return 0;
01568 }
01569
01570
01571 static const char *good_number(char *buf, pj_int32_t val)
01572 {
01573 if (val < 1000) {
01574 pj_ansi_sprintf(buf, "%d", val);
01575 } else if (val < 1000000) {
01576 pj_ansi_sprintf(buf, "%d.%dK",
01577 val / 1000,
01578 (val % 1000) / 100);
01579 } else {
01580 pj_ansi_sprintf(buf, "%d.%02dM",
01581 val / 1000000,
01582 (val % 1000000) / 10000);
01583 }
01584
01585 return buf;
01586 }
01587
01588
01589 static int server_thread(void *arg)
01590 {
01591 pj_time_val timeout = { 0, 1 };
01592 unsigned thread_index = (unsigned)(long)arg;
01593 pj_time_val last_report, next_report;
01594
01595 pj_gettimeofday(&last_report);
01596 next_report = last_report;
01597 next_report.sec++;
01598
01599 while (!app.thread_quit) {
01600 pj_time_val now;
01601 unsigned i;
01602
01603 for (i=0; i<100; ++i) {
01604 unsigned count = 0;
01605 pjsip_endpt_handle_events2(app.sip_endpt, &timeout, &count);
01606 if (count == 0)
01607 break;
01608 }
01609
01610 if (thread_index == 0) {
01611 pj_gettimeofday(&now);
01612
01613 if (PJ_TIME_VAL_GTE(now, next_report)) {
01614 pj_time_val tmp;
01615 unsigned msec;
01616 unsigned stateless, stateful, call;
01617 char str_stateless[32], str_stateful[32], str_call[32];
01618
01619 tmp = now;
01620 PJ_TIME_VAL_SUB(tmp, last_report);
01621 msec = PJ_TIME_VAL_MSEC(tmp);
01622
01623 last_report = now;
01624 next_report = last_report;
01625 next_report.sec++;
01626
01627 stateless = app.server.cur_state.stateless_cnt - app.server.prev_state.stateless_cnt;
01628 stateful = app.server.cur_state.stateful_cnt - app.server.prev_state.stateful_cnt;
01629 call = app.server.cur_state.call_cnt - app.server.prev_state.call_cnt;
01630
01631 good_number(str_stateless, app.server.cur_state.stateless_cnt);
01632 good_number(str_stateful, app.server.cur_state.stateful_cnt);
01633 good_number(str_call, app.server.cur_state.call_cnt);
01634
01635 printf("Total(rate): stateless:%s (%d/s), statefull:%s (%d/s), call:%s (%d/s) \r",
01636 str_stateless, stateless*1000/msec,
01637 str_stateful, stateful*1000/msec,
01638 str_call, call*1000/msec);
01639 fflush(stdout);
01640
01641 app.server.prev_state = app.server.cur_state;
01642 }
01643 }
01644 }
01645
01646 return 0;
01647 }
01648
01649 static void write_report(const char *msg)
01650 {
01651 puts(msg);
01652
01653 #if defined(PJ_WIN32) && PJ_WIN32!=0
01654 OutputDebugString(msg);
01655 OutputDebugString("\n");
01656 #endif
01657 }
01658
01659
01660 int main(int argc, char *argv[])
01661 {
01662 static char report[1024];
01663
01664 printf("PJSIP Performance Measurement Tool v%s\n"
01665 "(c)2006 pjsip.org\n\n",
01666 PJ_VERSION);
01667
01668 if (create_app() != 0)
01669 return 1;
01670
01671 if (init_options(argc, argv) != 0)
01672 return 1;
01673
01674 if (init_sip() != 0)
01675 return 1;
01676
01677 if (init_media() != 0)
01678 return 1;
01679
01680 pj_log_set_level(app.log_level);
01681
01682 if (app.log_level > 4) {
01683 pjsip_endpt_register_module(app.sip_endpt, &msg_logger);
01684 }
01685
01686
01687
01688 if (app.client.dst_uri.slen != 0) {
01689 if (app.client.method.id == PJSIP_INVITE_METHOD) {
01690 if (app.client.stateless) {
01691 PJ_LOG(3,(THIS_FILE,
01692 "Info: --stateless option makes no sense for INVITE,"
01693 " ignored."));
01694 }
01695 }
01696
01697 }
01698
01699
01700
01701 if (app.client.dst_uri.slen) {
01702
01703 pj_status_t status;
01704 char test_type[64];
01705 unsigned msec_req, msec_res;
01706 unsigned i;
01707
01708
01709 if (app.client.method.id == PJSIP_INVITE_METHOD) {
01710 pj_ansi_strcpy(test_type, "INVITE calls");
01711 } else if (app.client.stateless) {
01712 pj_ansi_sprintf(test_type, "stateless %.*s requests",
01713 (int)app.client.method.name.slen,
01714 app.client.method.name.ptr);
01715 } else {
01716 pj_ansi_sprintf(test_type, "stateful %.*s requests",
01717 (int)app.client.method.name.slen,
01718 app.client.method.name.ptr);
01719 }
01720
01721
01722 printf("Sending %d %s to '%.*s' with %d maximum outstanding jobs, please wait..\n",
01723 app.client.job_count, test_type,
01724 (int)app.client.dst_uri.slen, app.client.dst_uri.ptr,
01725 app.client.job_window);
01726
01727 for (i=0; i<app.thread_count; ++i) {
01728 status = pj_thread_create(app.pool, NULL, &client_thread,
01729 (void*)(long)i, 0, 0, &app.thread[i]);
01730 if (status != PJ_SUCCESS) {
01731 app_perror(THIS_FILE, "Unable to create thread", status);
01732 return 1;
01733 }
01734 }
01735
01736 for (i=0; i<app.thread_count; ++i) {
01737 pj_thread_join(app.thread[i]);
01738 app.thread[i] = NULL;
01739 }
01740
01741 if (app.client.last_completion.sec) {
01742 pj_time_val duration;
01743 duration = app.client.last_completion;
01744 PJ_TIME_VAL_SUB(duration, app.client.first_request);
01745 msec_res = PJ_TIME_VAL_MSEC(duration);
01746 } else {
01747 msec_res = app.client.timeout * 1000;
01748 }
01749
01750 if (msec_res == 0) msec_res = 1;
01751
01752 if (app.client.requests_sent.sec) {
01753 pj_time_val duration;
01754 duration = app.client.requests_sent;
01755 PJ_TIME_VAL_SUB(duration, app.client.first_request);
01756 msec_req = PJ_TIME_VAL_MSEC(duration);
01757 } else {
01758 msec_req = app.client.timeout * 1000;
01759 }
01760
01761 if (msec_req == 0) msec_req = 1;
01762
01763 if (app.client.job_submitted < app.client.job_count)
01764 puts("\ntimed-out!\n");
01765 else
01766 puts("\ndone.\n");
01767
01768 pj_ansi_snprintf(
01769 report, sizeof(report),
01770 "Total %d %s sent in %d ms at rate of %d/sec\n"
01771 "Total %d responses receieved in %d ms at rate of %d/sec:",
01772 app.client.job_submitted, test_type, msec_req,
01773 app.client.job_submitted * 1000 / msec_req,
01774 app.client.total_responses, msec_res,
01775 app.client.total_responses*1000/msec_res);
01776 write_report(report);
01777
01778
01779 pj_ansi_sprintf(report, "\nDetailed responses received:");
01780 write_report(report);
01781
01782 for (i=0; i<PJ_ARRAY_SIZE(app.client.response_codes); ++i) {
01783 const pj_str_t *reason;
01784
01785 if (app.client.response_codes[i] == 0)
01786 continue;
01787
01788 reason = pjsip_get_status_text(i);
01789 pj_ansi_snprintf( report, sizeof(report),
01790 " - %d responses: %7d (%.*s)",
01791 i, app.client.response_codes[i],
01792 (int)reason->slen, reason->ptr);
01793 write_report(report);
01794 }
01795
01796
01797 pj_ansi_snprintf( report, sizeof(report),
01798 " ------\n"
01799 " TOTAL responses: %7d (rate=%d/sec)\n",
01800 app.client.total_responses,
01801 app.client.total_responses*1000/msec_res);
01802
01803 write_report(report);
01804
01805 pj_ansi_sprintf(report, "Maximum outstanding job: %d",
01806 app.client.stat_max_window);
01807 write_report(report);
01808
01809
01810 } else {
01811
01812 char s[10], *unused;
01813 pj_status_t status;
01814 unsigned i;
01815
01816 puts("pjsip-perf started in server-mode");
01817
01818 printf("Receiving requests on the following URIs:\n"
01819 " sip:0@%.*s:%d%s for stateless handling\n"
01820 " sip:1@%.*s:%d%s for stateful handling\n"
01821 " sip:2@%.*s:%d%s for call handling\n",
01822 (int)app.local_addr.slen,
01823 app.local_addr.ptr,
01824 app.local_port,
01825 (app.use_tcp ? ";transport=tcp" : ""),
01826 (int)app.local_addr.slen,
01827 app.local_addr.ptr,
01828 app.local_port,
01829 (app.use_tcp ? ";transport=tcp" : ""),
01830 (int)app.local_addr.slen,
01831 app.local_addr.ptr,
01832 app.local_port,
01833 (app.use_tcp ? ";transport=tcp" : ""));
01834 printf("INVITE with non-matching user part will be handled call-statefully\n");
01835
01836 for (i=0; i<app.thread_count; ++i) {
01837 status = pj_thread_create(app.pool, NULL, &server_thread,
01838 (void*)(long)i, 0, 0, &app.thread[i]);
01839 if (status != PJ_SUCCESS) {
01840 app_perror(THIS_FILE, "Unable to create thread", status);
01841 return 1;
01842 }
01843 }
01844
01845 puts("\nPress <ENTER> to quit\n");
01846 fflush(stdout);
01847 unused = fgets(s, sizeof(s), stdin);
01848 PJ_UNUSED_ARG(unused);
01849
01850 app.thread_quit = PJ_TRUE;
01851 for (i=0; i<app.thread_count; ++i) {
01852 pj_thread_join(app.thread[i]);
01853 app.thread[i] = NULL;
01854 }
01855
01856 puts("");
01857 }
01858
01859
01860 destroy_app();
01861
01862 return 0;
01863 }
01864
PJMEDIA small footprint Open Source media stack
Copyright (C) 2006-2008 Teluu Inc.
|
|