blob: e2a001837b6324da20f9e9c9f15081cf5c7506fe [file] [log] [blame]
Benny Prijono514ca6b2006-07-03 01:30:01 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono514ca6b2006-07-03 01:30:01 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21
22/**
23 * \page page_pjsip_perf_c Samples: SIP Performance Benchmark
24 *
25 * <b>pjsip-perf</b> is a complete program to measure the
26 * performance of PJSIP or other SIP endpoints. It consists of two
27 * parts:
28 * - the server, to respond incoming requests, and
29 * - the client, who actively submits requests and measure the
30 * performance of the server.
31 *
32 * Both server and client part can run simultaneously, to measure the
33 * performance when both endpoints are co-located in a single program.
34 *
35 * The server accepts both INVITE and non-INVITE requests.
36 * The server exports several different types of URL, which would
37 * control how the request would be handled by the server:
38 * - URL with "0" as the user part will be handled statelessly.
39 * It should not be used with INVITE method.
40 * - URL with "1" as the user part will be handled statefully.
41 * If the request is an INVITE request, INVITE transaction will
42 * be created and 200/OK response will be sent, along with a valid
43 * SDP body. However, the SDP is just a static text body, and
44 * is not a proper SDP generated by PJMEDIA.
45 * - URL with "2" as the user part is only meaningful for INVITE
46 * requests, as it would be handled <b>call-statefully</b> by the
47 * server. For this URL, the server also would generate SDP dynamically
48 * and perform a proper SDP negotiation for the incoming call.
49 * Also for every call, server will limit the call duration to
50 * 10 seconds, on which the call will be terminated if the client
51 * doesn't hangup the call.
52 *
53 *
54 *
55 * This file is pjsip-apps/src/samples/pjsip-perf.c
56 *
57 * \includelineno pjsip-perf.c
58 */
59
60/* Include all headers. */
61#include <pjsip.h>
62#include <pjmedia.h>
63#include <pjmedia-codec.h>
64#include <pjsip_ua.h>
65#include <pjsip_simple.h>
66#include <pjlib-util.h>
67#include <pjlib.h>
68#include <stdio.h>
69
Benny Prijono1479b652006-07-03 14:18:17 +000070#if defined(PJ_WIN32) && PJ_WIN32!=0
71# include <windows.h>
72#endif
73
74#define THIS_FILE "pjsip-perf.c"
Benny Prijono4768c3c2008-02-22 11:10:17 +000075#define DEFAULT_COUNT (pjsip_cfg()->tsx.max_count/2>10000?10000:pjsip_cfg()->tsx.max_count/2)
Benny Prijonoc3573762006-07-10 21:39:24 +000076#define JOB_WINDOW 1000
Benny Prijono1479b652006-07-03 14:18:17 +000077#define TERMINATE_TSX(x,c)
78
79
80#ifndef CACHING_POOL_SIZE
81# define CACHING_POOL_SIZE (256*1024*1024)
82#endif
Benny Prijono514ca6b2006-07-03 01:30:01 +000083
84
85/* Static message body for INVITE, when stateful processing is
86 * invoked (instead of call-stateful, where SDP is generated
87 * dynamically.
88 */
89static pj_str_t dummy_sdp_str =
90{
91 "v=0\r\n"
92 "o=- 3360842071 3360842071 IN IP4 192.168.0.68\r\n"
93 "s=pjmedia\r\n"
94 "c=IN IP4 192.168.0.68\r\n"
95 "t=0 0\r\n"
Benny Prijonofc290a62006-08-13 18:20:55 +000096 "m=audio 4000 RTP/AVP 0 8 3 103 102 101\r\n"
Benny Prijono514ca6b2006-07-03 01:30:01 +000097 "a=rtcp:4001 IN IP4 192.168.0.68\r\n"
98 "a=rtpmap:103 speex/16000\r\n"
99 "a=rtpmap:102 speex/8000\r\n"
100 "a=rtpmap:3 GSM/8000\r\n"
101 "a=rtpmap:0 PCMU/8000\r\n"
102 "a=rtpmap:8 PCMA/8000\r\n"
103 "a=sendrecv\r\n"
104 "a=rtpmap:101 telephone-event/8000\r\n"
105 "a=fmtp:101 0-15\r\n",
106 0
107};
108
109static pj_str_t mime_application = { "application", 11};
110static pj_str_t mime_sdp = {"sdp", 3};
111
112
113struct srv_state
114{
115 unsigned stateless_cnt;
116 unsigned stateful_cnt;
117 unsigned call_cnt;
118};
119
120
121struct app
122{
123 pj_caching_pool cp;
124 pj_pool_t *pool;
Benny Prijonoc03a3c52006-07-03 02:31:10 +0000125 pj_bool_t use_tcp;
Benny Prijono514ca6b2006-07-03 01:30:01 +0000126 pj_str_t local_addr;
127 int local_port;
128 pjsip_endpoint *sip_endpt;
129 pjmedia_endpt *med_endpt;
130 pj_str_t local_uri;
131 pj_str_t local_contact;
132 unsigned skinfo_cnt;
133 pjmedia_sock_info skinfo[8];
134
135 pj_bool_t thread_quit;
136 unsigned thread_count;
137 pj_thread_t *thread[16];
138
139 pj_bool_t real_sdp;
140 pjmedia_sdp_session *dummy_sdp;
Benny Prijonoc3573762006-07-10 21:39:24 +0000141
142 int log_level;
Benny Prijono514ca6b2006-07-03 01:30:01 +0000143
144 struct {
145 pjsip_method method;
146 pj_str_t dst_uri;
147 pj_bool_t stateless;
148 unsigned timeout;
149 unsigned job_count,
150 job_submitted,
151 job_finished,
152 job_window;
Benny Prijono1479b652006-07-03 14:18:17 +0000153 unsigned stat_max_window;
Benny Prijono514ca6b2006-07-03 01:30:01 +0000154 pj_time_val first_request;
Benny Prijonoc3573762006-07-10 21:39:24 +0000155 pj_time_val requests_sent;
Benny Prijono514ca6b2006-07-03 01:30:01 +0000156 pj_time_val last_completion;
157 unsigned total_responses;
Benny Prijono49f682a2006-07-11 12:25:45 +0000158 unsigned response_codes[800];
Benny Prijono514ca6b2006-07-03 01:30:01 +0000159 } client;
160
161 struct {
Benny Prijonof521eb02006-08-06 23:07:25 +0000162 pj_bool_t send_trying;
163 pj_bool_t send_ringing;
164 unsigned delay;
Benny Prijono514ca6b2006-07-03 01:30:01 +0000165 struct srv_state prev_state;
166 struct srv_state cur_state;
167 } server;
168
169
170} app;
171
172struct call
173{
174 pjsip_inv_session *inv;
Benny Prijonof521eb02006-08-06 23:07:25 +0000175 pj_timer_entry ans_timer;
Benny Prijono514ca6b2006-07-03 01:30:01 +0000176};
177
178
179static void app_perror(const char *sender, const char *title,
180 pj_status_t status)
181{
182 char errmsg[PJ_ERR_MSG_SIZE];
183
184 pj_strerror(status, errmsg, sizeof(errmsg));
185 PJ_LOG(1,(sender, "%s: %s [code=%d]", title, errmsg, status));
186}
187
188
189/**************************************************************************
190 * STATELESS SERVER
191 */
192static pj_bool_t mod_stateless_on_rx_request(pjsip_rx_data *rdata);
193
194/* Module to handle incoming requests statelessly.
195 */
196static pjsip_module mod_stateless_server =
197{
198 NULL, NULL, /* prev, next. */
199 { "mod-stateless-server", 20 }, /* Name. */
200 -1, /* Id */
201 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
202 NULL, /* load() */
203 NULL, /* start() */
204 NULL, /* stop() */
205 NULL, /* unload() */
206 &mod_stateless_on_rx_request, /* on_rx_request() */
207 NULL, /* on_rx_response() */
208 NULL, /* on_tx_request. */
209 NULL, /* on_tx_response() */
210 NULL, /* on_tsx_state() */
211};
212
213
214static pj_bool_t mod_stateless_on_rx_request(pjsip_rx_data *rdata)
215{
216 const pj_str_t stateless_user = { "0", 1 };
217 pjsip_uri *uri;
218 pjsip_sip_uri *sip_uri;
219
220 uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
221
222 /* Only want to receive SIP scheme */
223 if (!PJSIP_URI_SCHEME_IS_SIP(uri))
224 return PJ_FALSE;
225
226 sip_uri = (pjsip_sip_uri*) uri;
227
228 /* Check for matching user part */
229 if (pj_strcmp(&sip_uri->user, &stateless_user)!=0)
230 return PJ_FALSE;
231
232 /*
233 * Yes, this is for us.
234 */
235
236 /* Ignore ACK request */
237 if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD)
238 return PJ_TRUE;
239
240 /*
241 * Respond statelessly with 200/OK.
242 */
243 pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 200, NULL,
244 NULL, NULL);
245 app.server.cur_state.stateless_cnt++;
246 return PJ_TRUE;
247}
248
249
250/**************************************************************************
251 * STATEFUL SERVER
252 */
253static pj_bool_t mod_stateful_on_rx_request(pjsip_rx_data *rdata);
254
255/* Module to handle incoming requests statefully.
256 */
257static pjsip_module mod_stateful_server =
258{
259 NULL, NULL, /* prev, next. */
260 { "mod-stateful-server", 19 }, /* Name. */
261 -1, /* Id */
262 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
263 NULL, /* load() */
264 NULL, /* start() */
265 NULL, /* stop() */
266 NULL, /* unload() */
267 &mod_stateful_on_rx_request, /* on_rx_request() */
268 NULL, /* on_rx_response() */
269 NULL, /* on_tx_request. */
270 NULL, /* on_tx_response() */
271 NULL, /* on_tsx_state() */
272};
273
274
275static pj_bool_t mod_stateful_on_rx_request(pjsip_rx_data *rdata)
276{
277 const pj_str_t stateful_user = { "1", 1 };
278 pjsip_uri *uri;
279 pjsip_sip_uri *sip_uri;
280
281 uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
282
283 /* Only want to receive SIP scheme */
284 if (!PJSIP_URI_SCHEME_IS_SIP(uri))
285 return PJ_FALSE;
286
287 sip_uri = (pjsip_sip_uri*) uri;
288
289 /* Check for matching user part */
290 if (pj_strcmp(&sip_uri->user, &stateful_user)!=0)
291 return PJ_FALSE;
292
293 /*
294 * Yes, this is for us.
295 * Respond statefully with 200/OK.
296 */
297 switch (rdata->msg_info.msg->line.req.method.id) {
298 case PJSIP_INVITE_METHOD:
299 {
300 pjsip_msg_body *body;
301
302 if (dummy_sdp_str.slen == 0)
303 dummy_sdp_str.slen = pj_ansi_strlen(dummy_sdp_str.ptr);
304
305 body = pjsip_msg_body_create(rdata->tp_info.pool,
306 &mime_application, &mime_sdp,
307 &dummy_sdp_str);
308 pjsip_endpt_respond(app.sip_endpt, &mod_stateful_server, rdata,
309 200, NULL, NULL, body, NULL);
310 }
311 break;
312 case PJSIP_ACK_METHOD:
313 return PJ_TRUE;
314 default:
315 pjsip_endpt_respond(app.sip_endpt, &mod_stateful_server, rdata,
316 200, NULL, NULL, NULL, NULL);
317 break;
318 }
319
320 app.server.cur_state.stateful_cnt++;
321 return PJ_TRUE;
322}
323
324
325/**************************************************************************
326 * CALL SERVER
327 */
328static pj_bool_t mod_call_on_rx_request(pjsip_rx_data *rdata);
329
330/* Module to handle incoming requests callly.
331 */
332static pjsip_module mod_call_server =
333{
334 NULL, NULL, /* prev, next. */
335 { "mod-call-server", 15 }, /* Name. */
336 -1, /* Id */
337 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
338 NULL, /* load() */
339 NULL, /* start() */
340 NULL, /* stop() */
341 NULL, /* unload() */
342 &mod_call_on_rx_request, /* on_rx_request() */
343 NULL, /* on_rx_response() */
344 NULL, /* on_tx_request. */
345 NULL, /* on_tx_response() */
346 NULL, /* on_tsx_state() */
347};
348
349
Benny Prijonof521eb02006-08-06 23:07:25 +0000350static pj_status_t send_response(pjsip_inv_session *inv,
351 pjsip_rx_data *rdata,
352 int code,
353 pj_bool_t *has_initial)
354{
355 pjsip_tx_data *tdata;
356 pj_status_t status;
357
358 if (*has_initial) {
359 status = pjsip_inv_answer(inv, code, NULL, NULL, &tdata);
360 } else {
361 status = pjsip_inv_initial_answer(inv, rdata, code,
362 NULL, NULL, &tdata);
363 }
364
365 if (status != PJ_SUCCESS) {
366 if (*has_initial) {
367 status = pjsip_inv_answer(inv, PJSIP_SC_NOT_ACCEPTABLE,
368 NULL, NULL, &tdata);
369 } else {
370 status = pjsip_inv_initial_answer(inv, rdata,
371 PJSIP_SC_NOT_ACCEPTABLE,
372 NULL, NULL, &tdata);
373 }
374
375 if (status == PJ_SUCCESS) {
376 *has_initial = PJ_TRUE;
377 pjsip_inv_send_msg(inv, tdata);
378 } else {
379 pjsip_inv_terminate(inv, 500, PJ_FALSE);
380 return -1;
381 }
382 } else {
383 *has_initial = PJ_TRUE;
384
385 status = pjsip_inv_send_msg(inv, tdata);
386 if (status != PJ_SUCCESS) {
387 pjsip_tx_data_dec_ref(tdata);
388 return status;
389 }
390 }
391
392 return status;
393}
394
395static void answer_timer_cb(pj_timer_heap_t *h, pj_timer_entry *entry)
396{
397 struct call *call = entry->user_data;
398 pj_bool_t has_initial = PJ_TRUE;
399
Benny Prijonoc1e263f2006-08-09 13:53:59 +0000400 PJ_UNUSED_ARG(h);
401
Benny Prijonof521eb02006-08-06 23:07:25 +0000402 entry->id = 0;
403 send_response(call->inv, NULL, 200, &has_initial);
404}
405
Benny Prijono514ca6b2006-07-03 01:30:01 +0000406static pj_bool_t mod_call_on_rx_request(pjsip_rx_data *rdata)
407{
408 const pj_str_t call_user = { "2", 1 };
409 pjsip_uri *uri;
410 pjsip_sip_uri *sip_uri;
411 struct call *call;
412 pjsip_dialog *dlg;
413 pjmedia_sdp_session *sdp;
414 pjsip_tx_data *tdata;
Benny Prijonof521eb02006-08-06 23:07:25 +0000415 pj_bool_t has_initial = PJ_FALSE;
Benny Prijono514ca6b2006-07-03 01:30:01 +0000416 pj_status_t status;
417
418 uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
419
420 /* Only want to receive SIP scheme */
421 if (!PJSIP_URI_SCHEME_IS_SIP(uri))
422 return PJ_FALSE;
423
424 sip_uri = (pjsip_sip_uri*) uri;
425
Benny Prijonodcc0cbf2006-07-16 10:40:37 +0000426 /* Only want to handle INVITE requests. */
Benny Prijono514ca6b2006-07-03 01:30:01 +0000427 if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) {
428 return PJ_FALSE;
429 }
430
431
Benny Prijonodcc0cbf2006-07-16 10:40:37 +0000432 /* Check for matching user part. Incoming requests will be handled
433 * call-statefully if:
434 * - user part is "2", or
435 * - user part is not "0" nor "1" and method is INVITE.
436 */
437 if (pj_strcmp(&sip_uri->user, &call_user) == 0 ||
438 sip_uri->user.slen != 1 ||
439 (*sip_uri->user.ptr != '0' && *sip_uri->user.ptr != '1'))
440 {
441 /* Match */
442
443 } else {
444 return PJ_FALSE;
445 }
446
447
Benny Prijono514ca6b2006-07-03 01:30:01 +0000448 /* Verify that we can handle the request. */
449 if (app.real_sdp) {
450 unsigned options = 0;
451 status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
452 app.sip_endpt, &tdata);
453 if (status != PJ_SUCCESS) {
454
455 /*
456 * No we can't handle the incoming INVITE request.
457 */
458
459 if (tdata) {
460 pjsip_response_addr res_addr;
461
462 pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
463 pjsip_endpt_send_response(app.sip_endpt, &res_addr, tdata,
464 NULL, NULL);
465
466 } else {
467
468 /* Respond with 500 (Internal Server Error) */
469 pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 500, NULL,
470 NULL, NULL);
471 }
472
473 return PJ_TRUE;
474 }
475 }
476
477 /* Create UAS dialog */
478 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
479 &app.local_contact, &dlg);
480 if (status != PJ_SUCCESS) {
481 const pj_str_t reason = pj_str("Unable to create dialog");
482 pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
483 500, &reason,
484 NULL, NULL);
485 return PJ_TRUE;
486 }
487
488 /* Alloc call structure. */
489 call = pj_pool_zalloc(dlg->pool, sizeof(struct call));
490
491 /* Create SDP from PJMEDIA */
492 if (app.real_sdp) {
493 status = pjmedia_endpt_create_sdp(app.med_endpt, rdata->tp_info.pool,
494 app.skinfo_cnt, app.skinfo,
495 &sdp);
496 } else {
497 sdp = app.dummy_sdp;
498 }
499
500 /* Create UAS invite session */
501 status = pjsip_inv_create_uas( dlg, rdata, sdp, 0, &call->inv);
502 if (status != PJ_SUCCESS) {
503 pjsip_dlg_create_response(dlg, rdata, 500, NULL, &tdata);
504 pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata);
505 return PJ_TRUE;
506 }
507
Benny Prijonof521eb02006-08-06 23:07:25 +0000508 /* Send 100/Trying if needed */
509 if (app.server.send_trying) {
510 status = send_response(call->inv, rdata, 100, &has_initial);
511 if (status != PJ_SUCCESS)
512 return PJ_TRUE;
Benny Prijono514ca6b2006-07-03 01:30:01 +0000513 }
514
Benny Prijonof521eb02006-08-06 23:07:25 +0000515 /* Send 180/Ringing if needed */
516 if (app.server.send_ringing) {
517 status = send_response(call->inv, rdata, 180, &has_initial);
518 if (status != PJ_SUCCESS)
519 return PJ_TRUE;
520 }
Benny Prijono514ca6b2006-07-03 01:30:01 +0000521
Benny Prijonof521eb02006-08-06 23:07:25 +0000522 /* Simulate call processing delay */
523 if (app.server.delay) {
524 pj_time_val delay;
Benny Prijono514ca6b2006-07-03 01:30:01 +0000525
Benny Prijonof521eb02006-08-06 23:07:25 +0000526 call->ans_timer.id = 1;
527 call->ans_timer.user_data = call;
528 call->ans_timer.cb = &answer_timer_cb;
529
530 delay.sec = 0;
531 delay.msec = app.server.delay;
532 pj_time_val_normalize(&delay);
533
534 pjsip_endpt_schedule_timer(app.sip_endpt, &call->ans_timer, &delay);
535
536 } else {
537 /* Send the 200 response immediately . */
Benny Prijonoc1e263f2006-08-09 13:53:59 +0000538 status = send_response(call->inv, rdata, 200, &has_initial);
Benny Prijonof521eb02006-08-06 23:07:25 +0000539 PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return PJ_TRUE);
540 }
Benny Prijono514ca6b2006-07-03 01:30:01 +0000541
542 /* Done */
543 app.server.cur_state.call_cnt++;
544
545 return PJ_TRUE;
546}
547
548
549
Benny Prijonodcc0cbf2006-07-16 10:40:37 +0000550/**************************************************************************
551 * Default handler when incoming request is not handled by any other
552 * modules.
553 */
554static pj_bool_t mod_responder_on_rx_request(pjsip_rx_data *rdata);
555
556/* Module to handle incoming requests statelessly.
557 */
558static pjsip_module mod_responder =
559{
560 NULL, NULL, /* prev, next. */
561 { "mod-responder", 13 }, /* Name. */
562 -1, /* Id */
563 PJSIP_MOD_PRIORITY_APPLICATION+1, /* Priority */
564 NULL, /* load() */
565 NULL, /* start() */
566 NULL, /* stop() */
567 NULL, /* unload() */
568 &mod_responder_on_rx_request, /* on_rx_request() */
569 NULL, /* on_rx_response() */
570 NULL, /* on_tx_request. */
571 NULL, /* on_tx_response() */
572 NULL, /* on_tsx_state() */
573};
574
575
576static pj_bool_t mod_responder_on_rx_request(pjsip_rx_data *rdata)
577{
578 const pj_str_t reason = pj_str("Not expecting request at this URI");
579
580 /*
Benny Prijono7db431e2006-07-23 14:38:49 +0000581 * Respond any requests (except ACK!) with 500.
Benny Prijonodcc0cbf2006-07-16 10:40:37 +0000582 */
Benny Prijono7db431e2006-07-23 14:38:49 +0000583 if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
584 pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 500, &reason,
585 NULL, NULL);
586 }
587
Benny Prijonodcc0cbf2006-07-16 10:40:37 +0000588 return PJ_TRUE;
589}
590
591
Benny Prijono514ca6b2006-07-03 01:30:01 +0000592
593/*****************************************************************************
594 * Below is a simple module to log all incoming and outgoing SIP messages
595 */
596
597
598/* Notification on incoming messages */
599static pj_bool_t logger_on_rx_msg(pjsip_rx_data *rdata)
600{
Benny Prijonoc3573762006-07-10 21:39:24 +0000601 PJ_LOG(3,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
Benny Prijono514ca6b2006-07-03 01:30:01 +0000602 "%.*s\n"
603 "--end msg--",
604 rdata->msg_info.len,
605 pjsip_rx_data_get_info(rdata),
Benny Prijonoc3573762006-07-10 21:39:24 +0000606 rdata->tp_info.transport->type_name,
Benny Prijono514ca6b2006-07-03 01:30:01 +0000607 rdata->pkt_info.src_name,
608 rdata->pkt_info.src_port,
609 (int)rdata->msg_info.len,
610 rdata->msg_info.msg_buf));
611
612 /* Always return false, otherwise messages will not get processed! */
613 return PJ_FALSE;
614}
615
616/* Notification on outgoing messages */
617static pj_status_t logger_on_tx_msg(pjsip_tx_data *tdata)
618{
619
620 /* Important note:
621 * tp_info field is only valid after outgoing messages has passed
622 * transport layer. So don't try to access tp_info when the module
623 * has lower priority than transport layer.
624 */
625
Benny Prijonoc3573762006-07-10 21:39:24 +0000626 PJ_LOG(3,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
Benny Prijono514ca6b2006-07-03 01:30:01 +0000627 "%.*s\n"
628 "--end msg--",
629 (tdata->buf.cur - tdata->buf.start),
630 pjsip_tx_data_get_info(tdata),
Benny Prijonoc3573762006-07-10 21:39:24 +0000631 tdata->tp_info.transport->type_name,
Benny Prijono514ca6b2006-07-03 01:30:01 +0000632 tdata->tp_info.dst_name,
633 tdata->tp_info.dst_port,
634 (int)(tdata->buf.cur - tdata->buf.start),
635 tdata->buf.start));
636
637 /* Always return success, otherwise message will not get sent! */
638 return PJ_SUCCESS;
639}
640
641/* The module instance. */
642static pjsip_module msg_logger =
643{
644 NULL, NULL, /* prev, next. */
645 { "mod-siprtp-log", 14 }, /* Name. */
646 -1, /* Id */
647 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
648 NULL, /* load() */
649 NULL, /* start() */
650 NULL, /* stop() */
651 NULL, /* unload() */
652 &logger_on_rx_msg, /* on_rx_request() */
653 &logger_on_rx_msg, /* on_rx_response() */
654 &logger_on_tx_msg, /* on_tx_request. */
655 &logger_on_tx_msg, /* on_tx_response() */
656 NULL, /* on_tsx_state() */
657
658};
659
660
661
662/**************************************************************************
663 * Test Client.
664 */
665
666static pj_bool_t mod_test_on_rx_response(pjsip_rx_data *rdata);
667
668static void call_on_media_update( pjsip_inv_session *inv,
669 pj_status_t status);
670static void call_on_state_changed( pjsip_inv_session *inv,
671 pjsip_event *e);
672static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e);
673
674
675/* Module to handle incoming requests callly.
676 */
677static pjsip_module mod_test =
678{
679 NULL, NULL, /* prev, next. */
680 { "mod-test", 8 }, /* Name. */
681 -1, /* Id */
682 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
683 NULL, /* load() */
684 NULL, /* start() */
685 NULL, /* stop() */
686 NULL, /* unload() */
687 NULL, /* on_rx_request() */
688 &mod_test_on_rx_response, /* on_rx_response() */
689 NULL, /* on_tx_request. */
690 NULL, /* on_tx_response() */
691 NULL, /* on_tsx_state() */
692};
693
694
695static void report_completion(int status_code)
696{
697 app.client.job_finished++;
Benny Prijono49f682a2006-07-11 12:25:45 +0000698 if (status_code >= 200 && status_code < 800)
699 app.client.response_codes[status_code]++;
Benny Prijono514ca6b2006-07-03 01:30:01 +0000700 app.client.total_responses++;
701 pj_gettimeofday(&app.client.last_completion);
702}
703
704
705/* Handler when response is received. */
706static pj_bool_t mod_test_on_rx_response(pjsip_rx_data *rdata)
707{
708 if (pjsip_rdata_get_tsx(rdata) == NULL) {
709 report_completion(rdata->msg_info.msg->line.status.code);
710 }
711
712 return PJ_TRUE;
713}
714
715
716/*
717 * Create app
718 */
719static pj_status_t create_app(void)
720{
721 pj_status_t status;
722
723 status = pj_init();
724 if (status != PJ_SUCCESS) {
725 app_perror(THIS_FILE, "Error initializing pjlib", status);
726 return status;
727 }
728
729 /* init PJLIB-UTIL: */
730 status = pjlib_util_init();
731 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
732
733 /* Must create a pool factory before we can allocate any memory. */
Benny Prijono1479b652006-07-03 14:18:17 +0000734 pj_caching_pool_init(&app.cp, &pj_pool_factory_default_policy,
735 CACHING_POOL_SIZE);
Benny Prijono514ca6b2006-07-03 01:30:01 +0000736
737 /* Create application pool for misc. */
738 app.pool = pj_pool_create(&app.cp.factory, "app", 1000, 1000, NULL);
739
740 /* Create the endpoint: */
741 status = pjsip_endpt_create(&app.cp.factory, pj_gethostname()->ptr,
742 &app.sip_endpt);
743 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
744
745
746 return status;
747}
748
749
750/*
751 * Init SIP stack
752 */
753static pj_status_t init_sip()
754{
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000755 pj_status_t status = -1;
Benny Prijono514ca6b2006-07-03 01:30:01 +0000756
Benny Prijonoc03a3c52006-07-03 02:31:10 +0000757 /* Add UDP/TCP transport. */
Benny Prijono514ca6b2006-07-03 01:30:01 +0000758 {
759 pj_sockaddr_in addr;
760 pjsip_host_port addrname;
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000761 const char *transport_type = NULL;
Benny Prijono514ca6b2006-07-03 01:30:01 +0000762
Benny Prijonoac623b32006-07-03 15:19:31 +0000763 pj_bzero(&addr, sizeof(addr));
Benny Prijono8ab968f2007-07-20 08:08:30 +0000764 addr.sin_family = pj_AF_INET();
Benny Prijono514ca6b2006-07-03 01:30:01 +0000765 addr.sin_addr.s_addr = 0;
766 addr.sin_port = pj_htons((pj_uint16_t)app.local_port);
767
768 if (app.local_addr.slen) {
769 addrname.host = app.local_addr;
770 addrname.port = 5060;
Benny Prijonoc03a3c52006-07-03 02:31:10 +0000771 }
Benny Prijono514ca6b2006-07-03 01:30:01 +0000772 if (app.local_port != 0)
773 addrname.port = app.local_port;
774
Benny Prijono3569c0d2007-04-06 10:29:20 +0000775 if (0) {
776#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
777 } else if (app.use_tcp) {
Benny Prijonoc03a3c52006-07-03 02:31:10 +0000778 pj_sockaddr_in local_addr;
779 pjsip_tpfactory *tpfactory;
780
781 transport_type = "tcp";
Benny Prijono1479b652006-07-03 14:18:17 +0000782 pj_sockaddr_in_init(&local_addr, 0, (pj_uint16_t)app.local_port);
Benny Prijonoc03a3c52006-07-03 02:31:10 +0000783 status = pjsip_tcp_transport_start(app.sip_endpt, &local_addr,
784 app.thread_count, &tpfactory);
785 if (status == PJ_SUCCESS) {
786 app.local_addr = tpfactory->addr_name.host;
787 app.local_port = tpfactory->addr_name.port;
788 }
Benny Prijono3569c0d2007-04-06 10:29:20 +0000789#endif
Benny Prijonoc03a3c52006-07-03 02:31:10 +0000790 } else {
791 pjsip_transport *tp;
792
793 transport_type = "udp";
794 status = pjsip_udp_transport_start(app.sip_endpt, &addr,
795 (app.local_addr.slen ? &addrname:NULL),
796 app.thread_count, &tp);
797 if (status == PJ_SUCCESS) {
798 app.local_addr = tp->local_name.host;
799 app.local_port = tp->local_name.port;
800 }
801
802 }
Benny Prijono514ca6b2006-07-03 01:30:01 +0000803 if (status != PJ_SUCCESS) {
Benny Prijonoc03a3c52006-07-03 02:31:10 +0000804 app_perror(THIS_FILE, "Unable to start transport", status);
Benny Prijono514ca6b2006-07-03 01:30:01 +0000805 return status;
806 }
807
Benny Prijono514ca6b2006-07-03 01:30:01 +0000808 app.local_uri.ptr = pj_pool_alloc(app.pool, 128);
809 app.local_uri.slen = pj_ansi_sprintf(app.local_uri.ptr,
Benny Prijonoc03a3c52006-07-03 02:31:10 +0000810 "<sip:pjsip-perf@%.*s:%d;transport=%s>",
811 (int)app.local_addr.slen,
812 app.local_addr.ptr,
813 app.local_port,
814 transport_type);
Benny Prijono514ca6b2006-07-03 01:30:01 +0000815
816 app.local_contact = app.local_uri;
817 }
818
819 /*
820 * Init transaction layer.
821 * This will create/initialize transaction hash tables etc.
822 */
823 status = pjsip_tsx_layer_init_module(app.sip_endpt);
824 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
825
826 /* Initialize UA layer. */
827 status = pjsip_ua_init_module( app.sip_endpt, NULL );
828 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
829
Benny Prijonoc4f62fd2008-02-28 21:46:22 +0000830 /* Initialize 100rel support */
831 status = pjsip_100rel_init_module(app.sip_endpt);
832 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
833
Benny Prijono514ca6b2006-07-03 01:30:01 +0000834 /* Init invite session module. */
835 {
836 pjsip_inv_callback inv_cb;
837
838 /* Init the callback for INVITE session: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000839 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijono514ca6b2006-07-03 01:30:01 +0000840 inv_cb.on_state_changed = &call_on_state_changed;
841 inv_cb.on_new_session = &call_on_forked;
842 inv_cb.on_media_update = &call_on_media_update;
843
844 /* Initialize invite session module: */
845 status = pjsip_inv_usage_init(app.sip_endpt, &inv_cb);
846 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
847 }
848
849 /* Register our module to receive incoming requests. */
850 status = pjsip_endpt_register_module( app.sip_endpt, &mod_test);
851 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
852
853
854 /* Register stateless server module */
855 status = pjsip_endpt_register_module( app.sip_endpt, &mod_stateless_server);
856 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
857
Benny Prijonodcc0cbf2006-07-16 10:40:37 +0000858 /* Register default responder module */
859 status = pjsip_endpt_register_module( app.sip_endpt, &mod_responder);
860 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono514ca6b2006-07-03 01:30:01 +0000861
862 /* Register stateless server module */
863 status = pjsip_endpt_register_module( app.sip_endpt, &mod_stateful_server);
864 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
865
866
867 /* Register call server module */
868 status = pjsip_endpt_register_module( app.sip_endpt, &mod_call_server);
869 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
870
871
872 /* Done */
873 return PJ_SUCCESS;
874}
875
876
877/*
878 * Destroy SIP
879 */
880static void destroy_app()
881{
882 unsigned i;
883
884 app.thread_quit = 1;
885 for (i=0; i<app.thread_count; ++i) {
886 if (app.thread[i]) {
887 pj_thread_join(app.thread[i]);
888 pj_thread_destroy(app.thread[i]);
889 app.thread[i] = NULL;
890 }
891 }
892
893 if (app.sip_endpt) {
894 pjsip_endpt_destroy(app.sip_endpt);
895 app.sip_endpt = NULL;
896 }
897
898 if (app.pool) {
899 pj_pool_release(app.pool);
900 app.pool = NULL;
Benny Prijono1ef06df2006-07-09 10:06:44 +0000901 PJ_LOG(3,(THIS_FILE, "Peak memory size: %uMB",
902 app.cp.peak_used_size / 1000000));
Benny Prijono514ca6b2006-07-03 01:30:01 +0000903 pj_caching_pool_destroy(&app.cp);
904 }
Benny Prijonoaf1bb1e2006-11-21 12:39:31 +0000905
906 /* Shutdown PJLIB */
907 pj_shutdown();
Benny Prijono514ca6b2006-07-03 01:30:01 +0000908}
909
910
911/*
912 * Init media stack.
913 */
914static pj_status_t init_media()
915{
916 unsigned i;
917 pj_uint16_t rtp_port;
918 pj_status_t status;
919
920
921 /* Initialize media endpoint so that at least error subsystem is properly
922 * initialized.
923 */
924 status = pjmedia_endpt_create(&app.cp.factory,
925 pjsip_endpt_get_ioqueue(app.sip_endpt), 0,
926 &app.med_endpt);
927 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
928
929
930 /* Must register all codecs to be supported */
Benny Prijono06d1d0e2007-01-27 18:09:28 +0000931#if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0
Benny Prijono514ca6b2006-07-03 01:30:01 +0000932 pjmedia_codec_g711_init(app.med_endpt);
Benny Prijono06d1d0e2007-01-27 18:09:28 +0000933#endif
934#if defined(PJMEDIA_HAS_GSM_CODEC) && PJMEDIA_HAS_GSM_CODEC!=0
Benny Prijono514ca6b2006-07-03 01:30:01 +0000935 pjmedia_codec_gsm_init(app.med_endpt);
Benny Prijono06d1d0e2007-01-27 18:09:28 +0000936#endif
937#if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0
Benny Prijono514ca6b2006-07-03 01:30:01 +0000938 pjmedia_codec_speex_init(app.med_endpt, PJMEDIA_SPEEX_NO_UWB, 3, 3);
Benny Prijono06d1d0e2007-01-27 18:09:28 +0000939#endif
Benny Prijono7ffd7752008-03-17 14:07:53 +0000940#if defined(PJMEDIA_HAS_G722_CODEC) && PJMEDIA_HAS_G722_CODEC!=0
941 pjmedia_codec_g722_init(app.med_endpt);
942#endif
Benny Prijono514ca6b2006-07-03 01:30:01 +0000943
944 /* Init dummy socket addresses */
945 app.skinfo_cnt = 0;
946 for (i=0, rtp_port=4000; i<PJ_ARRAY_SIZE(app.skinfo); ++i, rtp_port+=2) {
947 pjmedia_sock_info *skinfo;
948
949 skinfo = &app.skinfo[i];
950
Benny Prijono5de81bf2007-12-05 04:29:13 +0000951 pj_sockaddr_in_init(&skinfo->rtp_addr_name.ipv4, &app.local_addr,
Benny Prijono514ca6b2006-07-03 01:30:01 +0000952 (pj_uint16_t)rtp_port);
Benny Prijono5de81bf2007-12-05 04:29:13 +0000953 pj_sockaddr_in_init(&skinfo->rtp_addr_name.ipv4, &app.local_addr,
Benny Prijono514ca6b2006-07-03 01:30:01 +0000954 (pj_uint16_t)(rtp_port+1));
955 app.skinfo_cnt++;
956 }
957
958 /* Generate dummy SDP */
959 dummy_sdp_str.slen = pj_ansi_strlen(dummy_sdp_str.ptr);
960 status = pjmedia_sdp_parse(app.pool, dummy_sdp_str.ptr, dummy_sdp_str.slen,
961 &app.dummy_sdp);
962 if (status != PJ_SUCCESS) {
963 app_perror(THIS_FILE, "Error parsing dummy SDP", status);
964 return status;
965 }
966
967
968 /* Done */
969 return PJ_SUCCESS;
970}
971
972
973/* This is notification from the call about media negotiation
974 * status. This is called for client calls only.
975 */
976static void call_on_media_update( pjsip_inv_session *inv,
977 pj_status_t status)
978{
979 if (status != PJ_SUCCESS) {
980 pjsip_tx_data *tdata;
981 pj_status_t status;
982
983 status = pjsip_inv_end_session(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE,
984 NULL, &tdata);
985 if (status == PJ_SUCCESS && tdata)
986 status = pjsip_inv_send_msg(inv, tdata);
987 }
988}
989
990
991/* This is notification from the call when the call state has changed.
992 * This is called for client calls only.
993 */
994static void call_on_state_changed( pjsip_inv_session *inv,
995 pjsip_event *e)
996{
997 PJ_UNUSED_ARG(e);
998
999 /* Bail out if the session has been counted before */
1000 if (inv->mod_data[mod_test.id] != NULL)
1001 return;
1002
1003 /* Bail out if this is not an outgoing call */
1004 if (inv->role != PJSIP_UAC_ROLE)
1005 return;
1006
1007 if (inv->state == PJSIP_INV_STATE_CONFIRMED) {
1008 pjsip_tx_data *tdata;
1009 pj_status_t status;
1010
1011 //report_completion(200);
1012 //inv->mod_data[mod_test.id] = (void*)1;
1013
1014 status = pjsip_inv_end_session(inv, PJSIP_SC_OK, NULL, &tdata);
1015 if (status == PJ_SUCCESS && tdata)
1016 status = pjsip_inv_send_msg(inv, tdata);
1017
1018 } else if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
1019 report_completion(inv->cause);
1020 inv->mod_data[mod_test.id] = (void*)1;
1021 }
1022}
1023
1024
1025/* Not implemented for now */
1026static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e)
1027{
1028 /* Do nothing */
1029 PJ_UNUSED_ARG(inv);
1030 PJ_UNUSED_ARG(e);
1031}
1032
1033
1034/*
1035 * Make outgoing call.
1036 */
1037static pj_status_t make_call(const pj_str_t *dst_uri)
1038{
1039 struct call *call;
1040 pjsip_dialog *dlg;
1041 pjmedia_sdp_session *sdp;
1042 pjsip_tx_data *tdata;
1043 pj_status_t status;
1044
1045
1046 /* Create UAC dialog */
1047 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
1048 &app.local_uri, /* local URI */
1049 &app.local_contact, /* local Contact */
1050 dst_uri, /* remote URI */
1051 dst_uri, /* remote target */
1052 &dlg); /* dialog */
1053 if (status != PJ_SUCCESS) {
1054 return status;
1055 }
1056
1057 /* Create call */
1058 call = pj_pool_zalloc(dlg->pool, sizeof(struct call));
1059
1060 /* Create SDP */
1061 if (app.real_sdp) {
1062 status = pjmedia_endpt_create_sdp(app.med_endpt, dlg->pool, 1,
1063 app.skinfo, &sdp);
1064 if (status != PJ_SUCCESS) {
1065 pjsip_dlg_terminate(dlg);
1066 return status;
1067 }
1068 } else
1069 sdp = app.dummy_sdp;
1070
1071 /* Create the INVITE session. */
1072 status = pjsip_inv_create_uac( dlg, sdp, 0, &call->inv);
1073 if (status != PJ_SUCCESS) {
1074 pjsip_dlg_terminate(dlg);
1075 return status;
1076 }
1077
1078
1079 /* Create initial INVITE request.
1080 * This INVITE request will contain a perfectly good request and
1081 * an SDP body as well.
1082 */
1083 status = pjsip_inv_invite(call->inv, &tdata);
1084 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
1085
1086
1087 /* Send initial INVITE request.
1088 * From now on, the invite session's state will be reported to us
1089 * via the invite session callbacks.
1090 */
1091 status = pjsip_inv_send_msg(call->inv, tdata);
1092 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
1093
1094
1095 return PJ_SUCCESS;
1096}
1097
1098
1099/*
1100 * Verify that valid SIP url is given.
1101 */
1102static pj_status_t verify_sip_url(const char *c_url)
1103{
1104 pjsip_uri *p;
1105 pj_pool_t *pool;
1106 char *url;
1107 int len = (c_url ? pj_ansi_strlen(c_url) : 0);
1108
1109 if (!len) return -1;
1110
1111 pool = pj_pool_create(&app.cp.factory, "check%p", 1024, 0, NULL);
1112 if (!pool) return PJ_ENOMEM;
1113
1114 url = pj_pool_alloc(pool, len+1);
1115 pj_ansi_strcpy(url, c_url);
1116 url[len] = '\0';
1117
1118 p = pjsip_parse_uri(pool, url, len, 0);
1119 if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0)
1120 p = NULL;
1121
1122 pj_pool_release(pool);
1123 return p ? 0 : -1;
1124}
1125
1126
1127static void usage(void)
1128{
1129 printf(
1130 "Usage:\n"
1131 " pjsip-perf [OPTIONS] -- to start as server\n"
1132 " pjsip-perf [OPTIONS] URL -- to call server (possibly itself)\n"
1133 "\n"
1134 "where:\n"
Benny Prijonoc3573762006-07-10 21:39:24 +00001135 " URL The SIP URL to be contacted.\n"
Benny Prijono514ca6b2006-07-03 01:30:01 +00001136 "\n"
Benny Prijonoc3573762006-07-10 21:39:24 +00001137 "Client options:\n"
Benny Prijono775a1b22006-07-11 09:53:27 +00001138 " --method=METHOD, -m Set test method (set to INVITE for call benchmark)\n"
1139 " [default: OPTIONS]\n"
Benny Prijonoc3573762006-07-10 21:39:24 +00001140 " --count=N, -n Set total number of requests to initiate\n"
1141 " [default=%d]\n"
1142 " --stateless, -s Set to operate in stateless mode\n"
1143 " [default: stateful]\n"
1144 " --timeout=SEC, -t Set client timeout [default=60 sec]\n"
1145 " --window=COUNT, -w Set maximum outstanding job [default: %d]\n"
1146 "\n"
1147 "SDP options (client and server):\n"
Benny Prijono514ca6b2006-07-03 01:30:01 +00001148 " --real-sdp Generate real SDP from pjmedia, and also perform\n"
Benny Prijonoc3573762006-07-10 21:39:24 +00001149 " proper SDP negotiation [default: dummy]\n"
1150 "\n"
1151 "Client and Server options:\n"
1152 " --local-port=PORT, -p Set local port [default: 5060]\n"
Benny Prijono775a1b22006-07-11 09:53:27 +00001153 " --use-tcp, -T Use TCP instead of UDP. Note that when started as\n"
1154 " client, you must add ;transport=tcp parameter to URL\n"
1155 " [default: no]\n"
Benny Prijonoc3573762006-07-10 21:39:24 +00001156 " --thread-count=N Set number of worker threads [default=1]\n"
Benny Prijonof521eb02006-08-06 23:07:25 +00001157 " --trying Send 100/Trying response (server, default no)\n"
1158 " --ringing Send 180/Ringing response (server, default no)\n"
1159 " --delay=MS, -d Delay answering call by MS (server, default no)\n"
Benny Prijonoc3573762006-07-10 21:39:24 +00001160 "\n"
1161 "Misc options:\n"
Benny Prijono514ca6b2006-07-03 01:30:01 +00001162 " --help, -h Display this screen\n"
Benny Prijono775a1b22006-07-11 09:53:27 +00001163 " --verbose, -v Verbose logging (put more than once for even more)\n"
Benny Prijono514ca6b2006-07-03 01:30:01 +00001164 "\n"
1165 "When started as server, pjsip-perf can be contacted on the following URIs:\n"
Benny Prijonoc3573762006-07-10 21:39:24 +00001166 " - sip:0@server-addr To handle requests statelessly.\n"
1167 " - sip:1@server-addr To handle requests statefully.\n"
1168 " - sip:2@server-addr To handle INVITE call.\n",
Benny Prijono1479b652006-07-03 14:18:17 +00001169 DEFAULT_COUNT, JOB_WINDOW);
Benny Prijono514ca6b2006-07-03 01:30:01 +00001170}
1171
1172
1173static int my_atoi(const char *s)
1174{
1175 pj_str_t ss = pj_str((char*)s);
1176 return pj_strtoul(&ss);
1177}
1178
1179
1180static pj_status_t init_options(int argc, char *argv[])
1181{
Benny Prijonof521eb02006-08-06 23:07:25 +00001182 enum { OPT_THREAD_COUNT = 1, OPT_REAL_SDP, OPT_TRYING, OPT_RINGING };
Benny Prijono514ca6b2006-07-03 01:30:01 +00001183 struct pj_getopt_option long_options[] = {
1184 { "local-port", 1, 0, 'p' },
1185 { "count", 1, 0, 'c' },
1186 { "thread-count", 1, 0, OPT_THREAD_COUNT },
1187 { "method", 1, 0, 'm' },
1188 { "help", 0, 0, 'h' },
1189 { "stateless", 0, 0, 's' },
1190 { "timeout", 1, 0, 't' },
1191 { "real-sdp", 0, 0, OPT_REAL_SDP },
1192 { "verbose", 0, 0, 'v' },
Benny Prijonoc03a3c52006-07-03 02:31:10 +00001193 { "use-tcp", 0, 0, 'T' },
Benny Prijono1479b652006-07-03 14:18:17 +00001194 { "window", 1, 0, 'w' },
Benny Prijonof521eb02006-08-06 23:07:25 +00001195 { "delay", 1, 0, 'd' },
1196 { "trying", 0, 0, OPT_TRYING},
1197 { "ringing", 0, 0, OPT_RINGING},
Benny Prijono514ca6b2006-07-03 01:30:01 +00001198 { NULL, 0, 0, 0 },
1199 };
1200 int c;
1201 int option_index;
1202
1203 /* Init default application configs */
1204 app.local_port = 5060;
1205 app.thread_count = 1;
1206 app.client.job_count = DEFAULT_COUNT;
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001207 app.client.method = *pjsip_get_options_method();
Benny Prijono514ca6b2006-07-03 01:30:01 +00001208 app.client.job_window = c = JOB_WINDOW;
1209 app.client.timeout = 60;
Benny Prijonoc3573762006-07-10 21:39:24 +00001210 app.log_level = 3;
Benny Prijono514ca6b2006-07-03 01:30:01 +00001211
1212
1213 /* Parse options */
1214 pj_optind = 0;
Benny Prijonof521eb02006-08-06 23:07:25 +00001215 while((c=pj_getopt_long(argc,argv, "p:c:m:t:w:d:hsv",
Benny Prijono514ca6b2006-07-03 01:30:01 +00001216 long_options, &option_index))!=-1)
1217 {
1218 switch (c) {
1219 case 'p':
1220 app.local_port = my_atoi(pj_optarg);
1221 if (app.local_port < 0 || app.local_port > 65535) {
1222 PJ_LOG(3,(THIS_FILE, "Invalid --local-port %s", pj_optarg));
1223 return -1;
1224 }
1225 break;
1226
1227 case 'c':
1228 app.client.job_count = my_atoi(pj_optarg);
1229 if (app.client.job_count < 0) {
1230 PJ_LOG(3,(THIS_FILE, "Invalid --local-port %s", pj_optarg));
1231 return -1;
1232 }
Benny Prijono4768c3c2008-02-22 11:10:17 +00001233 if (app.client.job_count > pjsip_cfg()->tsx.max_count)
Benny Prijono514ca6b2006-07-03 01:30:01 +00001234 PJ_LOG(3,(THIS_FILE,
1235 "Warning: --count value (%d) exceeds maximum "
1236 "transaction count (%d)", app.client.job_count,
Benny Prijono4768c3c2008-02-22 11:10:17 +00001237 pjsip_cfg()->tsx.max_count));
Benny Prijono514ca6b2006-07-03 01:30:01 +00001238 break;
1239
1240 case OPT_THREAD_COUNT:
1241 app.thread_count = my_atoi(pj_optarg);
1242 if (app.thread_count < 1 || app.thread_count > 16) {
1243 PJ_LOG(3,(THIS_FILE, "Invalid --thread-count %s", pj_optarg));
1244 return -1;
1245 }
1246 break;
1247
1248 case 'm':
1249 {
1250 pj_str_t temp = pj_str((char*)pj_optarg);
1251 pjsip_method_init_np(&app.client.method, &temp);
1252 }
1253 break;
1254
1255 case 'h':
1256 usage();
1257 return -1;
1258
1259 case 's':
1260 app.client.stateless = PJ_TRUE;
1261 break;
1262
1263 case OPT_REAL_SDP:
1264 app.real_sdp = 1;
1265 break;
1266
1267 case 'v':
Benny Prijonoc3573762006-07-10 21:39:24 +00001268 app.log_level++;
Benny Prijono514ca6b2006-07-03 01:30:01 +00001269 break;
1270
1271 case 't':
1272 app.client.timeout = my_atoi(pj_optarg);
1273 if (app.client.timeout < 0 || app.client.timeout > 600) {
1274 PJ_LOG(3,(THIS_FILE, "Invalid --timeout %s", pj_optarg));
1275 return -1;
1276 }
1277 break;
1278
Benny Prijono1479b652006-07-03 14:18:17 +00001279 case 'w':
1280 app.client.job_window = my_atoi(pj_optarg);
1281 if (app.client.job_window <= 0) {
1282 PJ_LOG(3,(THIS_FILE, "Invalid --window %s", pj_optarg));
1283 return -1;
1284 }
1285 break;
1286
Benny Prijonoc03a3c52006-07-03 02:31:10 +00001287 case 'T':
1288 app.use_tcp = PJ_TRUE;
1289 break;
1290
Benny Prijonof521eb02006-08-06 23:07:25 +00001291 case 'd':
1292 app.server.delay = my_atoi(pj_optarg);
1293 if (app.server.delay > 3600) {
1294 PJ_LOG(3,(THIS_FILE, "I think --delay %s is too long",
1295 pj_optarg));
1296 return -1;
1297 }
1298 break;
1299
1300 case OPT_TRYING:
1301 app.server.send_trying = 1;
1302 break;
1303
1304 case OPT_RINGING:
1305 app.server.send_ringing = 1;
1306 break;
1307
Benny Prijono514ca6b2006-07-03 01:30:01 +00001308 default:
1309 PJ_LOG(1,(THIS_FILE,
1310 "Invalid argument. Use --help to see help"));
1311 return -1;
1312 }
1313 }
1314
1315 if (pj_optind != argc) {
1316
1317 if (verify_sip_url(argv[pj_optind]) != PJ_SUCCESS) {
1318 PJ_LOG(1,(THIS_FILE, "Invalid SIP URI %s", argv[pj_optind]));
1319 return -1;
1320 }
1321 app.client.dst_uri = pj_str(argv[pj_optind]);
1322
1323 pj_optind++;
1324
1325 }
1326
1327 if (pj_optind != argc) {
1328 PJ_LOG(1,(THIS_FILE, "Error: unknown options %s", argv[pj_optind]));
1329 return -1;
1330 }
1331
1332 return 0;
1333}
1334
1335
1336/* Send one stateless request */
1337static pj_status_t submit_stateless_job(void)
1338{
1339 pjsip_tx_data *tdata;
1340 pj_status_t status;
1341
1342 status = pjsip_endpt_create_request(app.sip_endpt, &app.client.method,
1343 &app.client.dst_uri, &app.local_uri,
1344 &app.client.dst_uri, &app.local_contact,
1345 NULL, -1, NULL, &tdata);
1346 if (status != PJ_SUCCESS) {
1347 app_perror(THIS_FILE, "Error creating request", status);
1348 report_completion(701);
1349 return status;
1350 }
1351
1352 status = pjsip_endpt_send_request_stateless(app.sip_endpt, tdata, NULL,
1353 NULL);
1354 if (status != PJ_SUCCESS) {
1355 pjsip_tx_data_dec_ref(tdata);
1356 app_perror(THIS_FILE, "Error sending stateless request", status);
1357 report_completion(701);
1358 return status;
1359 }
1360
1361 return PJ_SUCCESS;
1362}
1363
1364
1365/* This callback is called when client transaction state has changed */
1366static void tsx_completion_cb(void *token, pjsip_event *event)
1367{
1368 pjsip_transaction *tsx;
1369
1370 PJ_UNUSED_ARG(token);
1371
1372 if (event->type != PJSIP_EVENT_TSX_STATE)
1373 return;
1374
1375 tsx = event->body.tsx_state.tsx;
1376
1377 if (tsx->mod_data[mod_test.id] != NULL) {
1378 /* This transaction has been calculated before */
1379 return;
1380 }
1381
1382 if (tsx->state==PJSIP_TSX_STATE_TERMINATED) {
1383 report_completion(tsx->status_code);
1384 tsx->mod_data[mod_test.id] = (void*)1;
1385 }
1386 else if (tsx->method.id == PJSIP_INVITE_METHOD &&
1387 tsx->state == PJSIP_TSX_STATE_CONFIRMED) {
1388
1389 report_completion(tsx->status_code);
1390 tsx->mod_data[mod_test.id] = (void*)1;
1391
1392 } else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
1393
1394 report_completion(tsx->status_code);
1395 tsx->mod_data[mod_test.id] = (void*)1;
1396
Benny Prijono1479b652006-07-03 14:18:17 +00001397 TERMINATE_TSX(tsx, tsx->status_code);
Benny Prijono514ca6b2006-07-03 01:30:01 +00001398 }
1399}
1400
1401
1402/* Send one stateful request */
1403static pj_status_t submit_job(void)
1404{
1405 pjsip_tx_data *tdata;
1406 pj_status_t status;
1407
1408 status = pjsip_endpt_create_request(app.sip_endpt, &app.client.method,
1409 &app.client.dst_uri, &app.local_uri,
1410 &app.client.dst_uri, &app.local_contact,
1411 NULL, -1, NULL, &tdata);
1412 if (status != PJ_SUCCESS) {
1413 app_perror(THIS_FILE, "Error creating request", status);
1414 report_completion(701);
1415 return status;
1416 }
1417
1418 status = pjsip_endpt_send_request(app.sip_endpt, tdata, -1, NULL,
1419 &tsx_completion_cb);
1420 if (status != PJ_SUCCESS) {
1421 app_perror(THIS_FILE, "Error sending stateful request", status);
1422 //should have been reported by tsx_completion_cb().
1423 //report_completion(701);
Benny Prijono27f01dd2006-10-16 21:07:19 +00001424 //No longer necessary (r777)
1425 //pjsip_tx_data_dec_ref(tdata);
Benny Prijono514ca6b2006-07-03 01:30:01 +00001426 }
1427 return status;
1428}
1429
1430
1431/* Client worker thread */
1432static int client_thread(void *arg)
1433{
Benny Prijono775a1b22006-07-11 09:53:27 +00001434 pj_time_val end_time, last_report, now;
Benny Prijono7db431e2006-07-23 14:38:49 +00001435 unsigned thread_index = (unsigned)(long)arg;
Benny Prijono49f682a2006-07-11 12:25:45 +00001436 unsigned cycle = 0, last_cycle = 0;
Benny Prijono514ca6b2006-07-03 01:30:01 +00001437
1438 pj_thread_sleep(100);
1439
1440 pj_gettimeofday(&end_time);
1441 end_time.sec += app.client.timeout;
1442
Benny Prijono775a1b22006-07-11 09:53:27 +00001443 pj_gettimeofday(&last_report);
1444
Benny Prijono514ca6b2006-07-03 01:30:01 +00001445 if (app.client.first_request.sec == 0) {
1446 pj_gettimeofday(&app.client.first_request);
1447 }
1448
1449 /* Submit all jobs */
Benny Prijono775a1b22006-07-11 09:53:27 +00001450 while (app.client.job_submitted < app.client.job_count && !app.thread_quit){
Benny Prijonoc3573762006-07-10 21:39:24 +00001451 pj_time_val timeout = { 0, 1 };
Benny Prijono514ca6b2006-07-03 01:30:01 +00001452 unsigned i;
1453 int outstanding;
1454 pj_status_t status;
1455
Benny Prijono1479b652006-07-03 14:18:17 +00001456 /* Calculate current outstanding job */
1457 outstanding = app.client.job_submitted - app.client.job_finished;
1458
1459 /* Update stats on max outstanding jobs */
1460 if (outstanding > (int)app.client.stat_max_window)
1461 app.client.stat_max_window = outstanding;
1462
Benny Prijono514ca6b2006-07-03 01:30:01 +00001463 /* Wait if there are more pending jobs than allowed in the
Benny Prijono49f682a2006-07-11 12:25:45 +00001464 * window. But spawn a new job anyway if no events are happening
1465 * after we wait for some time.
Benny Prijono514ca6b2006-07-03 01:30:01 +00001466 */
Benny Prijono49f682a2006-07-11 12:25:45 +00001467 for (i=0; outstanding > (int)app.client.job_window && i<1000; ++i) {
1468 pj_time_val wait = { 0, 500 };
1469 unsigned count = 0;
1470
1471 pjsip_endpt_handle_events2(app.sip_endpt, &wait, &count);
Benny Prijono514ca6b2006-07-03 01:30:01 +00001472 outstanding = app.client.job_submitted - app.client.job_finished;
Benny Prijono49f682a2006-07-11 12:25:45 +00001473
1474 if (count == 0)
1475 break;
1476
1477 ++cycle;
Benny Prijono514ca6b2006-07-03 01:30:01 +00001478 }
1479
Benny Prijono1479b652006-07-03 14:18:17 +00001480
1481 /* Submit one job */
Benny Prijono514ca6b2006-07-03 01:30:01 +00001482 if (app.client.method.id == PJSIP_INVITE_METHOD) {
1483 status = make_call(&app.client.dst_uri);
1484 } else if (app.client.stateless) {
1485 status = submit_stateless_job();
1486 } else {
1487 status = submit_job();
1488 }
1489
1490 ++app.client.job_submitted;
Benny Prijono49f682a2006-07-11 12:25:45 +00001491 ++cycle;
Benny Prijono514ca6b2006-07-03 01:30:01 +00001492
Benny Prijono1479b652006-07-03 14:18:17 +00001493 /* Handle event */
1494 pjsip_endpt_handle_events2(app.sip_endpt, &timeout, NULL);
1495
Benny Prijono775a1b22006-07-11 09:53:27 +00001496 /* Check for time out, also print report */
Benny Prijono49f682a2006-07-11 12:25:45 +00001497 if (cycle - last_cycle >= 500) {
Benny Prijono1479b652006-07-03 14:18:17 +00001498 pj_gettimeofday(&now);
Benny Prijono775a1b22006-07-11 09:53:27 +00001499 if (PJ_TIME_VAL_GTE(now, end_time)) {
Benny Prijono514ca6b2006-07-03 01:30:01 +00001500 break;
Benny Prijono775a1b22006-07-11 09:53:27 +00001501 }
Benny Prijono49f682a2006-07-11 12:25:45 +00001502 last_cycle = cycle;
Benny Prijono775a1b22006-07-11 09:53:27 +00001503
1504
1505 if (thread_index == 0 && now.sec-last_report.sec >= 2) {
1506 printf("\r%d jobs started, %d completed... ",
1507 app.client.job_submitted, app.client.job_finished);
1508 fflush(stdout);
1509 last_report = now;
1510 }
Benny Prijono514ca6b2006-07-03 01:30:01 +00001511 }
1512 }
1513
Benny Prijonoc3573762006-07-10 21:39:24 +00001514 if (app.client.requests_sent.sec == 0) {
1515 pj_gettimeofday(&app.client.requests_sent);
1516 }
1517
1518
Benny Prijono775a1b22006-07-11 09:53:27 +00001519 if (thread_index == 0) {
1520 printf("\r%d jobs started, %d completed%s\n",
1521 app.client.job_submitted, app.client.job_finished,
1522 (app.client.job_submitted!=app.client.job_finished ?
1523 ", waiting..." : ".") );
1524 fflush(stdout);
1525 }
1526
Benny Prijono514ca6b2006-07-03 01:30:01 +00001527 /* Wait until all jobs completes, or timed out */
Benny Prijonoc3573762006-07-10 21:39:24 +00001528 pj_gettimeofday(&now);
Benny Prijono775a1b22006-07-11 09:53:27 +00001529 while (PJ_TIME_VAL_LT(now, end_time) &&
Benny Prijonoc3573762006-07-10 21:39:24 +00001530 app.client.job_finished < app.client.job_count &&
1531 !app.thread_quit)
1532 {
1533 pj_time_val timeout = { 0, 1 };
Benny Prijono514ca6b2006-07-03 01:30:01 +00001534 unsigned i;
1535
Benny Prijonoc3573762006-07-10 21:39:24 +00001536 for (i=0; i<1000; ++i) {
Benny Prijono775a1b22006-07-11 09:53:27 +00001537 unsigned count;
1538 count = 0;
1539 pjsip_endpt_handle_events2(app.sip_endpt, &timeout, &count);
1540 if (count == 0)
1541 break;
1542 }
1543
1544 pj_gettimeofday(&now);
1545 }
1546
Benny Prijono49f682a2006-07-11 12:25:45 +00001547 /* Wait couple of seconds to let jobs completes (e.g. ACKs to be sent) */
Benny Prijono775a1b22006-07-11 09:53:27 +00001548 pj_gettimeofday(&now);
1549 end_time = now;
1550 end_time.sec += 2;
Benny Prijono49f682a2006-07-11 12:25:45 +00001551 while (PJ_TIME_VAL_LT(now, end_time))
Benny Prijono775a1b22006-07-11 09:53:27 +00001552 {
1553 pj_time_val timeout = { 0, 1 };
1554 unsigned i;
1555
1556 for (i=0; i<1000; ++i) {
1557 unsigned count;
1558 count = 0;
1559 pjsip_endpt_handle_events2(app.sip_endpt, &timeout, &count);
1560 if (count == 0)
1561 break;
Benny Prijono514ca6b2006-07-03 01:30:01 +00001562 }
1563
1564 pj_gettimeofday(&now);
Benny Prijonoc3573762006-07-10 21:39:24 +00001565 }
Benny Prijono514ca6b2006-07-03 01:30:01 +00001566
1567 return 0;
1568}
1569
1570
1571static const char *good_number(char *buf, pj_int32_t val)
1572{
1573 if (val < 1000) {
1574 pj_ansi_sprintf(buf, "%d", val);
1575 } else if (val < 1000000) {
1576 pj_ansi_sprintf(buf, "%d.%dK",
1577 val / 1000,
1578 (val % 1000) / 100);
1579 } else {
1580 pj_ansi_sprintf(buf, "%d.%02dM",
1581 val / 1000000,
1582 (val % 1000000) / 10000);
1583 }
1584
1585 return buf;
1586}
1587
1588
1589static int server_thread(void *arg)
1590{
Benny Prijonoc3573762006-07-10 21:39:24 +00001591 pj_time_val timeout = { 0, 1 };
Benny Prijono7db431e2006-07-23 14:38:49 +00001592 unsigned thread_index = (unsigned)(long)arg;
Benny Prijono514ca6b2006-07-03 01:30:01 +00001593 pj_time_val last_report, next_report;
1594
1595 pj_gettimeofday(&last_report);
1596 next_report = last_report;
1597 next_report.sec++;
1598
1599 while (!app.thread_quit) {
1600 pj_time_val now;
1601 unsigned i;
1602
1603 for (i=0; i<100; ++i) {
1604 unsigned count = 0;
1605 pjsip_endpt_handle_events2(app.sip_endpt, &timeout, &count);
1606 if (count == 0)
1607 break;
1608 }
1609
1610 if (thread_index == 0) {
1611 pj_gettimeofday(&now);
1612
1613 if (PJ_TIME_VAL_GTE(now, next_report)) {
1614 pj_time_val tmp;
1615 unsigned msec;
1616 unsigned stateless, stateful, call;
1617 char str_stateless[32], str_stateful[32], str_call[32];
1618
1619 tmp = now;
1620 PJ_TIME_VAL_SUB(tmp, last_report);
1621 msec = PJ_TIME_VAL_MSEC(tmp);
1622
1623 last_report = now;
1624 next_report = last_report;
1625 next_report.sec++;
1626
1627 stateless = app.server.cur_state.stateless_cnt - app.server.prev_state.stateless_cnt;
1628 stateful = app.server.cur_state.stateful_cnt - app.server.prev_state.stateful_cnt;
1629 call = app.server.cur_state.call_cnt - app.server.prev_state.call_cnt;
1630
1631 good_number(str_stateless, app.server.cur_state.stateless_cnt);
1632 good_number(str_stateful, app.server.cur_state.stateful_cnt);
1633 good_number(str_call, app.server.cur_state.call_cnt);
1634
Benny Prijono1479b652006-07-03 14:18:17 +00001635 printf("Total(rate): stateless:%s (%d/s), statefull:%s (%d/s), call:%s (%d/s) \r",
Benny Prijono514ca6b2006-07-03 01:30:01 +00001636 str_stateless, stateless*1000/msec,
1637 str_stateful, stateful*1000/msec,
1638 str_call, call*1000/msec);
1639 fflush(stdout);
1640
1641 app.server.prev_state = app.server.cur_state;
1642 }
1643 }
1644 }
1645
1646 return 0;
1647}
1648
Benny Prijono1479b652006-07-03 14:18:17 +00001649static void write_report(const char *msg)
1650{
1651 puts(msg);
1652
1653#if defined(PJ_WIN32) && PJ_WIN32!=0
1654 OutputDebugString(msg);
1655 OutputDebugString("\n");
1656#endif
1657}
1658
1659
Benny Prijono514ca6b2006-07-03 01:30:01 +00001660int main(int argc, char *argv[])
1661{
Benny Prijono1479b652006-07-03 14:18:17 +00001662 static char report[1024];
Benny Prijono514ca6b2006-07-03 01:30:01 +00001663
Benny Prijonodcc0cbf2006-07-16 10:40:37 +00001664 printf("PJSIP Performance Measurement Tool v%s\n"
1665 "(c)2006 pjsip.org\n\n",
1666 PJ_VERSION);
Benny Prijono775a1b22006-07-11 09:53:27 +00001667
Benny Prijono514ca6b2006-07-03 01:30:01 +00001668 if (create_app() != 0)
1669 return 1;
1670
1671 if (init_options(argc, argv) != 0)
1672 return 1;
1673
1674 if (init_sip() != 0)
1675 return 1;
1676
1677 if (init_media() != 0)
1678 return 1;
1679
Benny Prijonoc3573762006-07-10 21:39:24 +00001680 pj_log_set_level(app.log_level);
1681
Benny Prijonodcc0cbf2006-07-16 10:40:37 +00001682 if (app.log_level > 4) {
Benny Prijono514ca6b2006-07-03 01:30:01 +00001683 pjsip_endpt_register_module(app.sip_endpt, &msg_logger);
1684 }
1685
1686
1687 /* Misc infos */
1688 if (app.client.dst_uri.slen != 0) {
Benny Prijonoc3573762006-07-10 21:39:24 +00001689 if (app.client.method.id == PJSIP_INVITE_METHOD) {
1690 if (app.client.stateless) {
1691 PJ_LOG(3,(THIS_FILE,
1692 "Info: --stateless option makes no sense for INVITE,"
1693 " ignored."));
1694 }
Benny Prijono514ca6b2006-07-03 01:30:01 +00001695 }
Benny Prijonoc3573762006-07-10 21:39:24 +00001696
Benny Prijono514ca6b2006-07-03 01:30:01 +00001697 }
1698
Benny Prijono514ca6b2006-07-03 01:30:01 +00001699
1700
1701 if (app.client.dst_uri.slen) {
1702 /* Client mode */
1703 pj_status_t status;
1704 char test_type[64];
Benny Prijonoc3573762006-07-10 21:39:24 +00001705 unsigned msec_req, msec_res;
Benny Prijono514ca6b2006-07-03 01:30:01 +00001706 unsigned i;
1707
1708 /* Get the job name */
1709 if (app.client.method.id == PJSIP_INVITE_METHOD) {
1710 pj_ansi_strcpy(test_type, "INVITE calls");
1711 } else if (app.client.stateless) {
1712 pj_ansi_sprintf(test_type, "stateless %.*s requests",
1713 (int)app.client.method.name.slen,
1714 app.client.method.name.ptr);
1715 } else {
1716 pj_ansi_sprintf(test_type, "stateful %.*s requests",
1717 (int)app.client.method.name.slen,
1718 app.client.method.name.ptr);
1719 }
1720
1721
Benny Prijono775a1b22006-07-11 09:53:27 +00001722 printf("Sending %d %s to '%.*s' with %d maximum outstanding jobs, please wait..\n",
Benny Prijonoc03a3c52006-07-03 02:31:10 +00001723 app.client.job_count, test_type,
Benny Prijonoc3573762006-07-10 21:39:24 +00001724 (int)app.client.dst_uri.slen, app.client.dst_uri.ptr,
Benny Prijono775a1b22006-07-11 09:53:27 +00001725 app.client.job_window);
Benny Prijono514ca6b2006-07-03 01:30:01 +00001726
1727 for (i=0; i<app.thread_count; ++i) {
Benny Prijono7db431e2006-07-23 14:38:49 +00001728 status = pj_thread_create(app.pool, NULL, &client_thread,
1729 (void*)(long)i, 0, 0, &app.thread[i]);
Benny Prijono514ca6b2006-07-03 01:30:01 +00001730 if (status != PJ_SUCCESS) {
1731 app_perror(THIS_FILE, "Unable to create thread", status);
1732 return 1;
1733 }
1734 }
1735
1736 for (i=0; i<app.thread_count; ++i) {
1737 pj_thread_join(app.thread[i]);
1738 app.thread[i] = NULL;
1739 }
1740
1741 if (app.client.last_completion.sec) {
1742 pj_time_val duration;
1743 duration = app.client.last_completion;
1744 PJ_TIME_VAL_SUB(duration, app.client.first_request);
Benny Prijonoc3573762006-07-10 21:39:24 +00001745 msec_res = PJ_TIME_VAL_MSEC(duration);
Benny Prijono514ca6b2006-07-03 01:30:01 +00001746 } else {
Benny Prijonoc3573762006-07-10 21:39:24 +00001747 msec_res = app.client.timeout * 1000;
Benny Prijono514ca6b2006-07-03 01:30:01 +00001748 }
1749
Benny Prijonoc3573762006-07-10 21:39:24 +00001750 if (msec_res == 0) msec_res = 1;
1751
1752 if (app.client.requests_sent.sec) {
1753 pj_time_val duration;
1754 duration = app.client.requests_sent;
1755 PJ_TIME_VAL_SUB(duration, app.client.first_request);
1756 msec_req = PJ_TIME_VAL_MSEC(duration);
1757 } else {
1758 msec_req = app.client.timeout * 1000;
1759 }
1760
1761 if (msec_req == 0) msec_req = 1;
1762
Benny Prijono775a1b22006-07-11 09:53:27 +00001763 if (app.client.job_submitted < app.client.job_count)
1764 puts("\ntimed-out!\n");
1765 else
1766 puts("\ndone.\n");
Benny Prijono514ca6b2006-07-03 01:30:01 +00001767
Benny Prijono1479b652006-07-03 14:18:17 +00001768 pj_ansi_snprintf(
1769 report, sizeof(report),
Benny Prijono775a1b22006-07-11 09:53:27 +00001770 "Total %d %s sent in %d ms at rate of %d/sec\n"
Benny Prijono49f682a2006-07-11 12:25:45 +00001771 "Total %d responses receieved in %d ms at rate of %d/sec:",
Benny Prijonoc3573762006-07-10 21:39:24 +00001772 app.client.job_submitted, test_type, msec_req,
1773 app.client.job_submitted * 1000 / msec_req,
1774 app.client.total_responses, msec_res,
Benny Prijono49f682a2006-07-11 12:25:45 +00001775 app.client.total_responses*1000/msec_res);
1776 write_report(report);
1777
1778 /* Print detailed response code received */
1779 pj_ansi_sprintf(report, "\nDetailed responses received:");
1780 write_report(report);
1781
1782 for (i=0; i<PJ_ARRAY_SIZE(app.client.response_codes); ++i) {
1783 const pj_str_t *reason;
1784
1785 if (app.client.response_codes[i] == 0)
1786 continue;
1787
1788 reason = pjsip_get_status_text(i);
1789 pj_ansi_snprintf( report, sizeof(report),
1790 " - %d responses: %7d (%.*s)",
1791 i, app.client.response_codes[i],
1792 (int)reason->slen, reason->ptr);
1793 write_report(report);
1794 }
1795
1796 /* Total responses and rate */
1797 pj_ansi_snprintf( report, sizeof(report),
1798 " ------\n"
1799 " TOTAL responses: %7d (rate=%d/sec)\n",
1800 app.client.total_responses,
1801 app.client.total_responses*1000/msec_res);
Benny Prijono1479b652006-07-03 14:18:17 +00001802
1803 write_report(report);
1804
1805 pj_ansi_sprintf(report, "Maximum outstanding job: %d",
1806 app.client.stat_max_window);
1807 write_report(report);
1808
Benny Prijono514ca6b2006-07-03 01:30:01 +00001809
1810 } else {
1811 /* Server mode */
1812 char s[10];
1813 pj_status_t status;
1814 unsigned i;
1815
Benny Prijono514ca6b2006-07-03 01:30:01 +00001816 puts("pjsip-perf started in server-mode");
1817
1818 printf("Receiving requests on the following URIs:\n"
Benny Prijono49f682a2006-07-11 12:25:45 +00001819 " sip:0@%.*s:%d%s for stateless handling\n"
1820 " sip:1@%.*s:%d%s for stateful handling\n"
1821 " sip:2@%.*s:%d%s for call handling\n",
Benny Prijono514ca6b2006-07-03 01:30:01 +00001822 (int)app.local_addr.slen,
1823 app.local_addr.ptr,
1824 app.local_port,
Benny Prijono49f682a2006-07-11 12:25:45 +00001825 (app.use_tcp ? ";transport=tcp" : ""),
Benny Prijono514ca6b2006-07-03 01:30:01 +00001826 (int)app.local_addr.slen,
1827 app.local_addr.ptr,
1828 app.local_port,
Benny Prijono49f682a2006-07-11 12:25:45 +00001829 (app.use_tcp ? ";transport=tcp" : ""),
Benny Prijono514ca6b2006-07-03 01:30:01 +00001830 (int)app.local_addr.slen,
1831 app.local_addr.ptr,
Benny Prijonoc03a3c52006-07-03 02:31:10 +00001832 app.local_port,
Benny Prijono49f682a2006-07-11 12:25:45 +00001833 (app.use_tcp ? ";transport=tcp" : ""));
Benny Prijonodcc0cbf2006-07-16 10:40:37 +00001834 printf("INVITE with non-matching user part will be handled call-statefully\n");
Benny Prijono514ca6b2006-07-03 01:30:01 +00001835
1836 for (i=0; i<app.thread_count; ++i) {
Benny Prijono7db431e2006-07-23 14:38:49 +00001837 status = pj_thread_create(app.pool, NULL, &server_thread,
1838 (void*)(long)i, 0, 0, &app.thread[i]);
Benny Prijono514ca6b2006-07-03 01:30:01 +00001839 if (status != PJ_SUCCESS) {
1840 app_perror(THIS_FILE, "Unable to create thread", status);
1841 return 1;
1842 }
1843 }
1844
Benny Prijono49f682a2006-07-11 12:25:45 +00001845 puts("\nPress <ENTER> to quit\n");
Benny Prijono514ca6b2006-07-03 01:30:01 +00001846 fflush(stdout);
1847 fgets(s, sizeof(s), stdin);
1848
1849 app.thread_quit = PJ_TRUE;
1850 for (i=0; i<app.thread_count; ++i) {
1851 pj_thread_join(app.thread[i]);
1852 app.thread[i] = NULL;
1853 }
Benny Prijono49f682a2006-07-11 12:25:45 +00001854
1855 puts("");
Benny Prijono514ca6b2006-07-03 01:30:01 +00001856 }
1857
1858
1859 destroy_app();
1860
1861 return 0;
1862}
1863