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