blob: 292e165371296ecfa6157a2156981af0b4badb12 [file] [log] [blame]
Benny Prijono514ca6b2006-07-03 01:30:01 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
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"
74#define DEFAULT_COUNT (PJSIP_MAX_TSX_COUNT/2>10000?10000:PJSIP_MAX_TSX_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"
95 "m=audio 4000 RTP/AVP 103 102 3 0 8 101\r\n"
96 "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;
157 unsigned status_class[7];
158 } client;
159
160 struct {
161 struct srv_state prev_state;
162 struct srv_state cur_state;
163 } server;
164
165
166} app;
167
168struct call
169{
170 pjsip_inv_session *inv;
171 pj_timer_entry timer;
172};
173
174
175static void app_perror(const char *sender, const char *title,
176 pj_status_t status)
177{
178 char errmsg[PJ_ERR_MSG_SIZE];
179
180 pj_strerror(status, errmsg, sizeof(errmsg));
181 PJ_LOG(1,(sender, "%s: %s [code=%d]", title, errmsg, status));
182}
183
184
185/**************************************************************************
186 * STATELESS SERVER
187 */
188static pj_bool_t mod_stateless_on_rx_request(pjsip_rx_data *rdata);
189
190/* Module to handle incoming requests statelessly.
191 */
192static pjsip_module mod_stateless_server =
193{
194 NULL, NULL, /* prev, next. */
195 { "mod-stateless-server", 20 }, /* Name. */
196 -1, /* Id */
197 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
198 NULL, /* load() */
199 NULL, /* start() */
200 NULL, /* stop() */
201 NULL, /* unload() */
202 &mod_stateless_on_rx_request, /* on_rx_request() */
203 NULL, /* on_rx_response() */
204 NULL, /* on_tx_request. */
205 NULL, /* on_tx_response() */
206 NULL, /* on_tsx_state() */
207};
208
209
210static pj_bool_t mod_stateless_on_rx_request(pjsip_rx_data *rdata)
211{
212 const pj_str_t stateless_user = { "0", 1 };
213 pjsip_uri *uri;
214 pjsip_sip_uri *sip_uri;
215
216 uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
217
218 /* Only want to receive SIP scheme */
219 if (!PJSIP_URI_SCHEME_IS_SIP(uri))
220 return PJ_FALSE;
221
222 sip_uri = (pjsip_sip_uri*) uri;
223
224 /* Check for matching user part */
225 if (pj_strcmp(&sip_uri->user, &stateless_user)!=0)
226 return PJ_FALSE;
227
228 /*
229 * Yes, this is for us.
230 */
231
232 /* Ignore ACK request */
233 if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD)
234 return PJ_TRUE;
235
236 /*
237 * Respond statelessly with 200/OK.
238 */
239 pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 200, NULL,
240 NULL, NULL);
241 app.server.cur_state.stateless_cnt++;
242 return PJ_TRUE;
243}
244
245
246/**************************************************************************
247 * STATEFUL SERVER
248 */
249static pj_bool_t mod_stateful_on_rx_request(pjsip_rx_data *rdata);
250
251/* Module to handle incoming requests statefully.
252 */
253static pjsip_module mod_stateful_server =
254{
255 NULL, NULL, /* prev, next. */
256 { "mod-stateful-server", 19 }, /* Name. */
257 -1, /* Id */
258 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
259 NULL, /* load() */
260 NULL, /* start() */
261 NULL, /* stop() */
262 NULL, /* unload() */
263 &mod_stateful_on_rx_request, /* on_rx_request() */
264 NULL, /* on_rx_response() */
265 NULL, /* on_tx_request. */
266 NULL, /* on_tx_response() */
267 NULL, /* on_tsx_state() */
268};
269
270
271static pj_bool_t mod_stateful_on_rx_request(pjsip_rx_data *rdata)
272{
273 const pj_str_t stateful_user = { "1", 1 };
274 pjsip_uri *uri;
275 pjsip_sip_uri *sip_uri;
276
277 uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
278
279 /* Only want to receive SIP scheme */
280 if (!PJSIP_URI_SCHEME_IS_SIP(uri))
281 return PJ_FALSE;
282
283 sip_uri = (pjsip_sip_uri*) uri;
284
285 /* Check for matching user part */
286 if (pj_strcmp(&sip_uri->user, &stateful_user)!=0)
287 return PJ_FALSE;
288
289 /*
290 * Yes, this is for us.
291 * Respond statefully with 200/OK.
292 */
293 switch (rdata->msg_info.msg->line.req.method.id) {
294 case PJSIP_INVITE_METHOD:
295 {
296 pjsip_msg_body *body;
297
298 if (dummy_sdp_str.slen == 0)
299 dummy_sdp_str.slen = pj_ansi_strlen(dummy_sdp_str.ptr);
300
301 body = pjsip_msg_body_create(rdata->tp_info.pool,
302 &mime_application, &mime_sdp,
303 &dummy_sdp_str);
304 pjsip_endpt_respond(app.sip_endpt, &mod_stateful_server, rdata,
305 200, NULL, NULL, body, NULL);
306 }
307 break;
308 case PJSIP_ACK_METHOD:
309 return PJ_TRUE;
310 default:
311 pjsip_endpt_respond(app.sip_endpt, &mod_stateful_server, rdata,
312 200, NULL, NULL, NULL, NULL);
313 break;
314 }
315
316 app.server.cur_state.stateful_cnt++;
317 return PJ_TRUE;
318}
319
320
321/**************************************************************************
322 * CALL SERVER
323 */
324static pj_bool_t mod_call_on_rx_request(pjsip_rx_data *rdata);
325
326/* Module to handle incoming requests callly.
327 */
328static pjsip_module mod_call_server =
329{
330 NULL, NULL, /* prev, next. */
331 { "mod-call-server", 15 }, /* Name. */
332 -1, /* Id */
333 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
334 NULL, /* load() */
335 NULL, /* start() */
336 NULL, /* stop() */
337 NULL, /* unload() */
338 &mod_call_on_rx_request, /* on_rx_request() */
339 NULL, /* on_rx_response() */
340 NULL, /* on_tx_request. */
341 NULL, /* on_tx_response() */
342 NULL, /* on_tsx_state() */
343};
344
345
346static pj_bool_t mod_call_on_rx_request(pjsip_rx_data *rdata)
347{
348 const pj_str_t call_user = { "2", 1 };
349 pjsip_uri *uri;
350 pjsip_sip_uri *sip_uri;
351 struct call *call;
352 pjsip_dialog *dlg;
353 pjmedia_sdp_session *sdp;
354 pjsip_tx_data *tdata;
355 pj_status_t status;
356
357 uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
358
359 /* Only want to receive SIP scheme */
360 if (!PJSIP_URI_SCHEME_IS_SIP(uri))
361 return PJ_FALSE;
362
363 sip_uri = (pjsip_sip_uri*) uri;
364
365 /* Check for matching user part */
366 if (pj_strcmp(&sip_uri->user, &call_user)!=0)
367 return PJ_FALSE;
368
369 /* Only want to handle INVITE requests (for now). */
370 if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) {
371 return PJ_FALSE;
372 }
373
374
375 /* Verify that we can handle the request. */
376 if (app.real_sdp) {
377 unsigned options = 0;
378 status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
379 app.sip_endpt, &tdata);
380 if (status != PJ_SUCCESS) {
381
382 /*
383 * No we can't handle the incoming INVITE request.
384 */
385
386 if (tdata) {
387 pjsip_response_addr res_addr;
388
389 pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
390 pjsip_endpt_send_response(app.sip_endpt, &res_addr, tdata,
391 NULL, NULL);
392
393 } else {
394
395 /* Respond with 500 (Internal Server Error) */
396 pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 500, NULL,
397 NULL, NULL);
398 }
399
400 return PJ_TRUE;
401 }
402 }
403
404 /* Create UAS dialog */
405 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
406 &app.local_contact, &dlg);
407 if (status != PJ_SUCCESS) {
408 const pj_str_t reason = pj_str("Unable to create dialog");
409 pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
410 500, &reason,
411 NULL, NULL);
412 return PJ_TRUE;
413 }
414
415 /* Alloc call structure. */
416 call = pj_pool_zalloc(dlg->pool, sizeof(struct call));
417
418 /* Create SDP from PJMEDIA */
419 if (app.real_sdp) {
420 status = pjmedia_endpt_create_sdp(app.med_endpt, rdata->tp_info.pool,
421 app.skinfo_cnt, app.skinfo,
422 &sdp);
423 } else {
424 sdp = app.dummy_sdp;
425 }
426
427 /* Create UAS invite session */
428 status = pjsip_inv_create_uas( dlg, rdata, sdp, 0, &call->inv);
429 if (status != PJ_SUCCESS) {
430 pjsip_dlg_create_response(dlg, rdata, 500, NULL, &tdata);
431 pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata);
432 return PJ_TRUE;
433 }
434
435
436 /* Create 200 response .*/
437 status = pjsip_inv_initial_answer(call->inv, rdata, 200,
438 NULL, NULL, &tdata);
439 if (status != PJ_SUCCESS) {
440 status = pjsip_inv_initial_answer(call->inv, rdata,
441 PJSIP_SC_NOT_ACCEPTABLE,
442 NULL, NULL, &tdata);
443 if (status == PJ_SUCCESS)
444 pjsip_inv_send_msg(call->inv, tdata);
445 else
446 pjsip_inv_terminate(call->inv, 500, PJ_FALSE);
447 return PJ_TRUE;
448 }
449
450
451 /* Send the 200 response. */
452 status = pjsip_inv_send_msg(call->inv, tdata);
453 PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return PJ_TRUE);
454
455
456 /* Done */
457 app.server.cur_state.call_cnt++;
458
459 return PJ_TRUE;
460}
461
462
463
464
465/*****************************************************************************
466 * Below is a simple module to log all incoming and outgoing SIP messages
467 */
468
469
470/* Notification on incoming messages */
471static pj_bool_t logger_on_rx_msg(pjsip_rx_data *rdata)
472{
Benny Prijonoc3573762006-07-10 21:39:24 +0000473 PJ_LOG(3,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
Benny Prijono514ca6b2006-07-03 01:30:01 +0000474 "%.*s\n"
475 "--end msg--",
476 rdata->msg_info.len,
477 pjsip_rx_data_get_info(rdata),
Benny Prijonoc3573762006-07-10 21:39:24 +0000478 rdata->tp_info.transport->type_name,
Benny Prijono514ca6b2006-07-03 01:30:01 +0000479 rdata->pkt_info.src_name,
480 rdata->pkt_info.src_port,
481 (int)rdata->msg_info.len,
482 rdata->msg_info.msg_buf));
483
484 /* Always return false, otherwise messages will not get processed! */
485 return PJ_FALSE;
486}
487
488/* Notification on outgoing messages */
489static pj_status_t logger_on_tx_msg(pjsip_tx_data *tdata)
490{
491
492 /* Important note:
493 * tp_info field is only valid after outgoing messages has passed
494 * transport layer. So don't try to access tp_info when the module
495 * has lower priority than transport layer.
496 */
497
Benny Prijonoc3573762006-07-10 21:39:24 +0000498 PJ_LOG(3,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
Benny Prijono514ca6b2006-07-03 01:30:01 +0000499 "%.*s\n"
500 "--end msg--",
501 (tdata->buf.cur - tdata->buf.start),
502 pjsip_tx_data_get_info(tdata),
Benny Prijonoc3573762006-07-10 21:39:24 +0000503 tdata->tp_info.transport->type_name,
Benny Prijono514ca6b2006-07-03 01:30:01 +0000504 tdata->tp_info.dst_name,
505 tdata->tp_info.dst_port,
506 (int)(tdata->buf.cur - tdata->buf.start),
507 tdata->buf.start));
508
509 /* Always return success, otherwise message will not get sent! */
510 return PJ_SUCCESS;
511}
512
513/* The module instance. */
514static pjsip_module msg_logger =
515{
516 NULL, NULL, /* prev, next. */
517 { "mod-siprtp-log", 14 }, /* Name. */
518 -1, /* Id */
519 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
520 NULL, /* load() */
521 NULL, /* start() */
522 NULL, /* stop() */
523 NULL, /* unload() */
524 &logger_on_rx_msg, /* on_rx_request() */
525 &logger_on_rx_msg, /* on_rx_response() */
526 &logger_on_tx_msg, /* on_tx_request. */
527 &logger_on_tx_msg, /* on_tx_response() */
528 NULL, /* on_tsx_state() */
529
530};
531
532
533
534/**************************************************************************
535 * Test Client.
536 */
537
538static pj_bool_t mod_test_on_rx_response(pjsip_rx_data *rdata);
539
540static void call_on_media_update( pjsip_inv_session *inv,
541 pj_status_t status);
542static void call_on_state_changed( pjsip_inv_session *inv,
543 pjsip_event *e);
544static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e);
545
546
547/* Module to handle incoming requests callly.
548 */
549static pjsip_module mod_test =
550{
551 NULL, NULL, /* prev, next. */
552 { "mod-test", 8 }, /* Name. */
553 -1, /* Id */
554 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
555 NULL, /* load() */
556 NULL, /* start() */
557 NULL, /* stop() */
558 NULL, /* unload() */
559 NULL, /* on_rx_request() */
560 &mod_test_on_rx_response, /* on_rx_response() */
561 NULL, /* on_tx_request. */
562 NULL, /* on_tx_response() */
563 NULL, /* on_tsx_state() */
564};
565
566
567static void report_completion(int status_code)
568{
569 app.client.job_finished++;
570 app.client.status_class[status_code/100]++;
571 app.client.total_responses++;
572 pj_gettimeofday(&app.client.last_completion);
573}
574
575
576/* Handler when response is received. */
577static pj_bool_t mod_test_on_rx_response(pjsip_rx_data *rdata)
578{
579 if (pjsip_rdata_get_tsx(rdata) == NULL) {
580 report_completion(rdata->msg_info.msg->line.status.code);
581 }
582
583 return PJ_TRUE;
584}
585
586
587/*
588 * Create app
589 */
590static pj_status_t create_app(void)
591{
592 pj_status_t status;
593
594 status = pj_init();
595 if (status != PJ_SUCCESS) {
596 app_perror(THIS_FILE, "Error initializing pjlib", status);
597 return status;
598 }
599
600 /* init PJLIB-UTIL: */
601 status = pjlib_util_init();
602 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
603
604 /* Must create a pool factory before we can allocate any memory. */
Benny Prijono1479b652006-07-03 14:18:17 +0000605 pj_caching_pool_init(&app.cp, &pj_pool_factory_default_policy,
606 CACHING_POOL_SIZE);
Benny Prijono514ca6b2006-07-03 01:30:01 +0000607
608 /* Create application pool for misc. */
609 app.pool = pj_pool_create(&app.cp.factory, "app", 1000, 1000, NULL);
610
611 /* Create the endpoint: */
612 status = pjsip_endpt_create(&app.cp.factory, pj_gethostname()->ptr,
613 &app.sip_endpt);
614 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
615
616
617 return status;
618}
619
620
621/*
622 * Init SIP stack
623 */
624static pj_status_t init_sip()
625{
626 pj_status_t status;
627
Benny Prijonoc03a3c52006-07-03 02:31:10 +0000628 /* Add UDP/TCP transport. */
Benny Prijono514ca6b2006-07-03 01:30:01 +0000629 {
630 pj_sockaddr_in addr;
631 pjsip_host_port addrname;
Benny Prijonoc03a3c52006-07-03 02:31:10 +0000632 const char *transport_type;
Benny Prijono514ca6b2006-07-03 01:30:01 +0000633
Benny Prijonoac623b32006-07-03 15:19:31 +0000634 pj_bzero(&addr, sizeof(addr));
Benny Prijono514ca6b2006-07-03 01:30:01 +0000635 addr.sin_family = PJ_AF_INET;
636 addr.sin_addr.s_addr = 0;
637 addr.sin_port = pj_htons((pj_uint16_t)app.local_port);
638
639 if (app.local_addr.slen) {
640 addrname.host = app.local_addr;
641 addrname.port = 5060;
Benny Prijonoc03a3c52006-07-03 02:31:10 +0000642 }
Benny Prijono514ca6b2006-07-03 01:30:01 +0000643 if (app.local_port != 0)
644 addrname.port = app.local_port;
645
Benny Prijonoc03a3c52006-07-03 02:31:10 +0000646 if (app.use_tcp) {
647 pj_sockaddr_in local_addr;
648 pjsip_tpfactory *tpfactory;
649
650 transport_type = "tcp";
Benny Prijono1479b652006-07-03 14:18:17 +0000651 pj_sockaddr_in_init(&local_addr, 0, (pj_uint16_t)app.local_port);
Benny Prijonoc03a3c52006-07-03 02:31:10 +0000652 status = pjsip_tcp_transport_start(app.sip_endpt, &local_addr,
653 app.thread_count, &tpfactory);
654 if (status == PJ_SUCCESS) {
655 app.local_addr = tpfactory->addr_name.host;
656 app.local_port = tpfactory->addr_name.port;
657 }
658 } else {
659 pjsip_transport *tp;
660
661 transport_type = "udp";
662 status = pjsip_udp_transport_start(app.sip_endpt, &addr,
663 (app.local_addr.slen ? &addrname:NULL),
664 app.thread_count, &tp);
665 if (status == PJ_SUCCESS) {
666 app.local_addr = tp->local_name.host;
667 app.local_port = tp->local_name.port;
668 }
669
670 }
Benny Prijono514ca6b2006-07-03 01:30:01 +0000671 if (status != PJ_SUCCESS) {
Benny Prijonoc03a3c52006-07-03 02:31:10 +0000672 app_perror(THIS_FILE, "Unable to start transport", status);
Benny Prijono514ca6b2006-07-03 01:30:01 +0000673 return status;
674 }
675
Benny Prijono514ca6b2006-07-03 01:30:01 +0000676 app.local_uri.ptr = pj_pool_alloc(app.pool, 128);
677 app.local_uri.slen = pj_ansi_sprintf(app.local_uri.ptr,
Benny Prijonoc03a3c52006-07-03 02:31:10 +0000678 "<sip:pjsip-perf@%.*s:%d;transport=%s>",
679 (int)app.local_addr.slen,
680 app.local_addr.ptr,
681 app.local_port,
682 transport_type);
Benny Prijono514ca6b2006-07-03 01:30:01 +0000683
684 app.local_contact = app.local_uri;
685 }
686
687 /*
688 * Init transaction layer.
689 * This will create/initialize transaction hash tables etc.
690 */
691 status = pjsip_tsx_layer_init_module(app.sip_endpt);
692 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
693
694 /* Initialize UA layer. */
695 status = pjsip_ua_init_module( app.sip_endpt, NULL );
696 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
697
698 /* Init invite session module. */
699 {
700 pjsip_inv_callback inv_cb;
701
702 /* Init the callback for INVITE session: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000703 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijono514ca6b2006-07-03 01:30:01 +0000704 inv_cb.on_state_changed = &call_on_state_changed;
705 inv_cb.on_new_session = &call_on_forked;
706 inv_cb.on_media_update = &call_on_media_update;
707
708 /* Initialize invite session module: */
709 status = pjsip_inv_usage_init(app.sip_endpt, &inv_cb);
710 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
711 }
712
713 /* Register our module to receive incoming requests. */
714 status = pjsip_endpt_register_module( app.sip_endpt, &mod_test);
715 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
716
717
718 /* Register stateless server module */
719 status = pjsip_endpt_register_module( app.sip_endpt, &mod_stateless_server);
720 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
721
722
723 /* Register stateless server module */
724 status = pjsip_endpt_register_module( app.sip_endpt, &mod_stateful_server);
725 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
726
727
728 /* Register call server module */
729 status = pjsip_endpt_register_module( app.sip_endpt, &mod_call_server);
730 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
731
732
733 /* Done */
734 return PJ_SUCCESS;
735}
736
737
738/*
739 * Destroy SIP
740 */
741static void destroy_app()
742{
743 unsigned i;
744
745 app.thread_quit = 1;
746 for (i=0; i<app.thread_count; ++i) {
747 if (app.thread[i]) {
748 pj_thread_join(app.thread[i]);
749 pj_thread_destroy(app.thread[i]);
750 app.thread[i] = NULL;
751 }
752 }
753
754 if (app.sip_endpt) {
755 pjsip_endpt_destroy(app.sip_endpt);
756 app.sip_endpt = NULL;
757 }
758
759 if (app.pool) {
760 pj_pool_release(app.pool);
761 app.pool = NULL;
Benny Prijono1ef06df2006-07-09 10:06:44 +0000762 PJ_LOG(3,(THIS_FILE, "Peak memory size: %uMB",
763 app.cp.peak_used_size / 1000000));
Benny Prijono514ca6b2006-07-03 01:30:01 +0000764 pj_caching_pool_destroy(&app.cp);
765 }
766}
767
768
769/*
770 * Init media stack.
771 */
772static pj_status_t init_media()
773{
774 unsigned i;
775 pj_uint16_t rtp_port;
776 pj_status_t status;
777
778
779 /* Initialize media endpoint so that at least error subsystem is properly
780 * initialized.
781 */
782 status = pjmedia_endpt_create(&app.cp.factory,
783 pjsip_endpt_get_ioqueue(app.sip_endpt), 0,
784 &app.med_endpt);
785 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
786
787
788 /* Must register all codecs to be supported */
789 pjmedia_codec_g711_init(app.med_endpt);
790 pjmedia_codec_gsm_init(app.med_endpt);
791 pjmedia_codec_speex_init(app.med_endpt, PJMEDIA_SPEEX_NO_UWB, 3, 3);
792
793
794 /* Init dummy socket addresses */
795 app.skinfo_cnt = 0;
796 for (i=0, rtp_port=4000; i<PJ_ARRAY_SIZE(app.skinfo); ++i, rtp_port+=2) {
797 pjmedia_sock_info *skinfo;
798
799 skinfo = &app.skinfo[i];
800
801 pj_sockaddr_in_init(&skinfo->rtp_addr_name, &app.local_addr,
802 (pj_uint16_t)rtp_port);
803 pj_sockaddr_in_init(&skinfo->rtp_addr_name, &app.local_addr,
804 (pj_uint16_t)(rtp_port+1));
805 app.skinfo_cnt++;
806 }
807
808 /* Generate dummy SDP */
809 dummy_sdp_str.slen = pj_ansi_strlen(dummy_sdp_str.ptr);
810 status = pjmedia_sdp_parse(app.pool, dummy_sdp_str.ptr, dummy_sdp_str.slen,
811 &app.dummy_sdp);
812 if (status != PJ_SUCCESS) {
813 app_perror(THIS_FILE, "Error parsing dummy SDP", status);
814 return status;
815 }
816
817
818 /* Done */
819 return PJ_SUCCESS;
820}
821
822
823/* This is notification from the call about media negotiation
824 * status. This is called for client calls only.
825 */
826static void call_on_media_update( pjsip_inv_session *inv,
827 pj_status_t status)
828{
829 if (status != PJ_SUCCESS) {
830 pjsip_tx_data *tdata;
831 pj_status_t status;
832
833 status = pjsip_inv_end_session(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE,
834 NULL, &tdata);
835 if (status == PJ_SUCCESS && tdata)
836 status = pjsip_inv_send_msg(inv, tdata);
837 }
838}
839
840
841/* This is notification from the call when the call state has changed.
842 * This is called for client calls only.
843 */
844static void call_on_state_changed( pjsip_inv_session *inv,
845 pjsip_event *e)
846{
847 PJ_UNUSED_ARG(e);
848
849 /* Bail out if the session has been counted before */
850 if (inv->mod_data[mod_test.id] != NULL)
851 return;
852
853 /* Bail out if this is not an outgoing call */
854 if (inv->role != PJSIP_UAC_ROLE)
855 return;
856
857 if (inv->state == PJSIP_INV_STATE_CONFIRMED) {
858 pjsip_tx_data *tdata;
859 pj_status_t status;
860
861 //report_completion(200);
862 //inv->mod_data[mod_test.id] = (void*)1;
863
864 status = pjsip_inv_end_session(inv, PJSIP_SC_OK, NULL, &tdata);
865 if (status == PJ_SUCCESS && tdata)
866 status = pjsip_inv_send_msg(inv, tdata);
867
868 } else if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
869 report_completion(inv->cause);
870 inv->mod_data[mod_test.id] = (void*)1;
871 }
872}
873
874
875/* Not implemented for now */
876static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e)
877{
878 /* Do nothing */
879 PJ_UNUSED_ARG(inv);
880 PJ_UNUSED_ARG(e);
881}
882
883
884/*
885 * Make outgoing call.
886 */
887static pj_status_t make_call(const pj_str_t *dst_uri)
888{
889 struct call *call;
890 pjsip_dialog *dlg;
891 pjmedia_sdp_session *sdp;
892 pjsip_tx_data *tdata;
893 pj_status_t status;
894
895
896 /* Create UAC dialog */
897 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
898 &app.local_uri, /* local URI */
899 &app.local_contact, /* local Contact */
900 dst_uri, /* remote URI */
901 dst_uri, /* remote target */
902 &dlg); /* dialog */
903 if (status != PJ_SUCCESS) {
904 return status;
905 }
906
907 /* Create call */
908 call = pj_pool_zalloc(dlg->pool, sizeof(struct call));
909
910 /* Create SDP */
911 if (app.real_sdp) {
912 status = pjmedia_endpt_create_sdp(app.med_endpt, dlg->pool, 1,
913 app.skinfo, &sdp);
914 if (status != PJ_SUCCESS) {
915 pjsip_dlg_terminate(dlg);
916 return status;
917 }
918 } else
919 sdp = app.dummy_sdp;
920
921 /* Create the INVITE session. */
922 status = pjsip_inv_create_uac( dlg, sdp, 0, &call->inv);
923 if (status != PJ_SUCCESS) {
924 pjsip_dlg_terminate(dlg);
925 return status;
926 }
927
928
929 /* Create initial INVITE request.
930 * This INVITE request will contain a perfectly good request and
931 * an SDP body as well.
932 */
933 status = pjsip_inv_invite(call->inv, &tdata);
934 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
935
936
937 /* Send initial INVITE request.
938 * From now on, the invite session's state will be reported to us
939 * via the invite session callbacks.
940 */
941 status = pjsip_inv_send_msg(call->inv, tdata);
942 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
943
944
945 return PJ_SUCCESS;
946}
947
948
949/*
950 * Verify that valid SIP url is given.
951 */
952static pj_status_t verify_sip_url(const char *c_url)
953{
954 pjsip_uri *p;
955 pj_pool_t *pool;
956 char *url;
957 int len = (c_url ? pj_ansi_strlen(c_url) : 0);
958
959 if (!len) return -1;
960
961 pool = pj_pool_create(&app.cp.factory, "check%p", 1024, 0, NULL);
962 if (!pool) return PJ_ENOMEM;
963
964 url = pj_pool_alloc(pool, len+1);
965 pj_ansi_strcpy(url, c_url);
966 url[len] = '\0';
967
968 p = pjsip_parse_uri(pool, url, len, 0);
969 if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0)
970 p = NULL;
971
972 pj_pool_release(pool);
973 return p ? 0 : -1;
974}
975
976
977static void usage(void)
978{
979 printf(
980 "Usage:\n"
981 " pjsip-perf [OPTIONS] -- to start as server\n"
982 " pjsip-perf [OPTIONS] URL -- to call server (possibly itself)\n"
983 "\n"
984 "where:\n"
Benny Prijonoc3573762006-07-10 21:39:24 +0000985 " URL The SIP URL to be contacted.\n"
Benny Prijono514ca6b2006-07-03 01:30:01 +0000986 "\n"
Benny Prijonoc3573762006-07-10 21:39:24 +0000987 "Client options:\n"
988 " --method=METHOD, -m Set the test method [default: OPTIONS]\n"
989 " --count=N, -n Set total number of requests to initiate\n"
990 " [default=%d]\n"
991 " --stateless, -s Set to operate in stateless mode\n"
992 " [default: stateful]\n"
993 " --timeout=SEC, -t Set client timeout [default=60 sec]\n"
994 " --window=COUNT, -w Set maximum outstanding job [default: %d]\n"
995 "\n"
996 "SDP options (client and server):\n"
Benny Prijono514ca6b2006-07-03 01:30:01 +0000997 " --real-sdp Generate real SDP from pjmedia, and also perform\n"
Benny Prijonoc3573762006-07-10 21:39:24 +0000998 " proper SDP negotiation [default: dummy]\n"
999 "\n"
1000 "Client and Server options:\n"
1001 " --local-port=PORT, -p Set local port [default: 5060]\n"
1002 " --use-tcp, -T Use TCP instead of UDP [default: no]\n"
1003 " --thread-count=N Set number of worker threads [default=1]\n"
1004 "\n"
1005 "Misc options:\n"
Benny Prijono514ca6b2006-07-03 01:30:01 +00001006 " --help, -h Display this screen\n"
Benny Prijonoc3573762006-07-10 21:39:24 +00001007 " --verbose, -v Display verbose logging (can be put more than once)\n"
Benny Prijono514ca6b2006-07-03 01:30:01 +00001008 "\n"
1009 "When started as server, pjsip-perf can be contacted on the following URIs:\n"
Benny Prijonoc3573762006-07-10 21:39:24 +00001010 " - sip:0@server-addr To handle requests statelessly.\n"
1011 " - sip:1@server-addr To handle requests statefully.\n"
1012 " - sip:2@server-addr To handle INVITE call.\n",
Benny Prijono1479b652006-07-03 14:18:17 +00001013 DEFAULT_COUNT, JOB_WINDOW);
Benny Prijono514ca6b2006-07-03 01:30:01 +00001014}
1015
1016
1017static int my_atoi(const char *s)
1018{
1019 pj_str_t ss = pj_str((char*)s);
1020 return pj_strtoul(&ss);
1021}
1022
1023
1024static pj_status_t init_options(int argc, char *argv[])
1025{
1026 enum { OPT_THREAD_COUNT = 1, OPT_REAL_SDP };
1027 struct pj_getopt_option long_options[] = {
1028 { "local-port", 1, 0, 'p' },
1029 { "count", 1, 0, 'c' },
1030 { "thread-count", 1, 0, OPT_THREAD_COUNT },
1031 { "method", 1, 0, 'm' },
1032 { "help", 0, 0, 'h' },
1033 { "stateless", 0, 0, 's' },
1034 { "timeout", 1, 0, 't' },
1035 { "real-sdp", 0, 0, OPT_REAL_SDP },
1036 { "verbose", 0, 0, 'v' },
Benny Prijonoc03a3c52006-07-03 02:31:10 +00001037 { "use-tcp", 0, 0, 'T' },
Benny Prijono1479b652006-07-03 14:18:17 +00001038 { "window", 1, 0, 'w' },
Benny Prijono514ca6b2006-07-03 01:30:01 +00001039 { NULL, 0, 0, 0 },
1040 };
1041 int c;
1042 int option_index;
1043
1044 /* Init default application configs */
1045 app.local_port = 5060;
1046 app.thread_count = 1;
1047 app.client.job_count = DEFAULT_COUNT;
1048 app.client.method = pjsip_options_method;
1049 app.client.job_window = c = JOB_WINDOW;
1050 app.client.timeout = 60;
Benny Prijonoc3573762006-07-10 21:39:24 +00001051 app.log_level = 3;
Benny Prijono514ca6b2006-07-03 01:30:01 +00001052
1053
1054 /* Parse options */
1055 pj_optind = 0;
Benny Prijono1479b652006-07-03 14:18:17 +00001056 while((c=pj_getopt_long(argc,argv, "p:c:m:t:w:hsv",
Benny Prijono514ca6b2006-07-03 01:30:01 +00001057 long_options, &option_index))!=-1)
1058 {
1059 switch (c) {
1060 case 'p':
1061 app.local_port = my_atoi(pj_optarg);
1062 if (app.local_port < 0 || app.local_port > 65535) {
1063 PJ_LOG(3,(THIS_FILE, "Invalid --local-port %s", pj_optarg));
1064 return -1;
1065 }
1066 break;
1067
1068 case 'c':
1069 app.client.job_count = my_atoi(pj_optarg);
1070 if (app.client.job_count < 0) {
1071 PJ_LOG(3,(THIS_FILE, "Invalid --local-port %s", pj_optarg));
1072 return -1;
1073 }
1074 if (app.client.job_count > PJSIP_MAX_TSX_COUNT)
1075 PJ_LOG(3,(THIS_FILE,
1076 "Warning: --count value (%d) exceeds maximum "
1077 "transaction count (%d)", app.client.job_count,
1078 PJSIP_MAX_TSX_COUNT));
1079 break;
1080
1081 case OPT_THREAD_COUNT:
1082 app.thread_count = my_atoi(pj_optarg);
1083 if (app.thread_count < 1 || app.thread_count > 16) {
1084 PJ_LOG(3,(THIS_FILE, "Invalid --thread-count %s", pj_optarg));
1085 return -1;
1086 }
1087 break;
1088
1089 case 'm':
1090 {
1091 pj_str_t temp = pj_str((char*)pj_optarg);
1092 pjsip_method_init_np(&app.client.method, &temp);
1093 }
1094 break;
1095
1096 case 'h':
1097 usage();
1098 return -1;
1099
1100 case 's':
1101 app.client.stateless = PJ_TRUE;
1102 break;
1103
1104 case OPT_REAL_SDP:
1105 app.real_sdp = 1;
1106 break;
1107
1108 case 'v':
Benny Prijonoc3573762006-07-10 21:39:24 +00001109 app.log_level++;
Benny Prijono514ca6b2006-07-03 01:30:01 +00001110 break;
1111
1112 case 't':
1113 app.client.timeout = my_atoi(pj_optarg);
1114 if (app.client.timeout < 0 || app.client.timeout > 600) {
1115 PJ_LOG(3,(THIS_FILE, "Invalid --timeout %s", pj_optarg));
1116 return -1;
1117 }
1118 break;
1119
Benny Prijono1479b652006-07-03 14:18:17 +00001120 case 'w':
1121 app.client.job_window = my_atoi(pj_optarg);
1122 if (app.client.job_window <= 0) {
1123 PJ_LOG(3,(THIS_FILE, "Invalid --window %s", pj_optarg));
1124 return -1;
1125 }
1126 break;
1127
Benny Prijonoc03a3c52006-07-03 02:31:10 +00001128 case 'T':
1129 app.use_tcp = PJ_TRUE;
1130 break;
1131
Benny Prijono514ca6b2006-07-03 01:30:01 +00001132 default:
1133 PJ_LOG(1,(THIS_FILE,
1134 "Invalid argument. Use --help to see help"));
1135 return -1;
1136 }
1137 }
1138
1139 if (pj_optind != argc) {
1140
1141 if (verify_sip_url(argv[pj_optind]) != PJ_SUCCESS) {
1142 PJ_LOG(1,(THIS_FILE, "Invalid SIP URI %s", argv[pj_optind]));
1143 return -1;
1144 }
1145 app.client.dst_uri = pj_str(argv[pj_optind]);
1146
1147 pj_optind++;
1148
1149 }
1150
1151 if (pj_optind != argc) {
1152 PJ_LOG(1,(THIS_FILE, "Error: unknown options %s", argv[pj_optind]));
1153 return -1;
1154 }
1155
1156 return 0;
1157}
1158
1159
1160/* Send one stateless request */
1161static pj_status_t submit_stateless_job(void)
1162{
1163 pjsip_tx_data *tdata;
1164 pj_status_t status;
1165
1166 status = pjsip_endpt_create_request(app.sip_endpt, &app.client.method,
1167 &app.client.dst_uri, &app.local_uri,
1168 &app.client.dst_uri, &app.local_contact,
1169 NULL, -1, NULL, &tdata);
1170 if (status != PJ_SUCCESS) {
1171 app_perror(THIS_FILE, "Error creating request", status);
1172 report_completion(701);
1173 return status;
1174 }
1175
1176 status = pjsip_endpt_send_request_stateless(app.sip_endpt, tdata, NULL,
1177 NULL);
1178 if (status != PJ_SUCCESS) {
1179 pjsip_tx_data_dec_ref(tdata);
1180 app_perror(THIS_FILE, "Error sending stateless request", status);
1181 report_completion(701);
1182 return status;
1183 }
1184
1185 return PJ_SUCCESS;
1186}
1187
1188
1189/* This callback is called when client transaction state has changed */
1190static void tsx_completion_cb(void *token, pjsip_event *event)
1191{
1192 pjsip_transaction *tsx;
1193
1194 PJ_UNUSED_ARG(token);
1195
1196 if (event->type != PJSIP_EVENT_TSX_STATE)
1197 return;
1198
1199 tsx = event->body.tsx_state.tsx;
1200
1201 if (tsx->mod_data[mod_test.id] != NULL) {
1202 /* This transaction has been calculated before */
1203 return;
1204 }
1205
1206 if (tsx->state==PJSIP_TSX_STATE_TERMINATED) {
1207 report_completion(tsx->status_code);
1208 tsx->mod_data[mod_test.id] = (void*)1;
1209 }
1210 else if (tsx->method.id == PJSIP_INVITE_METHOD &&
1211 tsx->state == PJSIP_TSX_STATE_CONFIRMED) {
1212
1213 report_completion(tsx->status_code);
1214 tsx->mod_data[mod_test.id] = (void*)1;
1215
1216 } else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
1217
1218 report_completion(tsx->status_code);
1219 tsx->mod_data[mod_test.id] = (void*)1;
1220
Benny Prijono1479b652006-07-03 14:18:17 +00001221 TERMINATE_TSX(tsx, tsx->status_code);
Benny Prijono514ca6b2006-07-03 01:30:01 +00001222 }
1223}
1224
1225
1226/* Send one stateful request */
1227static pj_status_t submit_job(void)
1228{
1229 pjsip_tx_data *tdata;
1230 pj_status_t status;
1231
1232 status = pjsip_endpt_create_request(app.sip_endpt, &app.client.method,
1233 &app.client.dst_uri, &app.local_uri,
1234 &app.client.dst_uri, &app.local_contact,
1235 NULL, -1, NULL, &tdata);
1236 if (status != PJ_SUCCESS) {
1237 app_perror(THIS_FILE, "Error creating request", status);
1238 report_completion(701);
1239 return status;
1240 }
1241
1242 status = pjsip_endpt_send_request(app.sip_endpt, tdata, -1, NULL,
1243 &tsx_completion_cb);
1244 if (status != PJ_SUCCESS) {
1245 app_perror(THIS_FILE, "Error sending stateful request", status);
1246 //should have been reported by tsx_completion_cb().
1247 //report_completion(701);
1248 pjsip_tx_data_dec_ref(tdata);
1249 }
1250 return status;
1251}
1252
1253
1254/* Client worker thread */
1255static int client_thread(void *arg)
1256{
Benny Prijono1479b652006-07-03 14:18:17 +00001257 unsigned last_timeout_check = 0;
Benny Prijono514ca6b2006-07-03 01:30:01 +00001258 pj_time_val end_time, now;
1259
1260 PJ_UNUSED_ARG(arg);
1261
1262 pj_thread_sleep(100);
1263
1264 pj_gettimeofday(&end_time);
1265 end_time.sec += app.client.timeout;
1266
1267 if (app.client.first_request.sec == 0) {
1268 pj_gettimeofday(&app.client.first_request);
1269 }
1270
1271 /* Submit all jobs */
1272 while (app.client.job_submitted < app.client.job_count && !app.thread_quit) {
Benny Prijonoc3573762006-07-10 21:39:24 +00001273 pj_time_val timeout = { 0, 1 };
Benny Prijono514ca6b2006-07-03 01:30:01 +00001274 unsigned i;
1275 int outstanding;
1276 pj_status_t status;
1277
Benny Prijono1479b652006-07-03 14:18:17 +00001278 /* Calculate current outstanding job */
1279 outstanding = app.client.job_submitted - app.client.job_finished;
1280
1281 /* Update stats on max outstanding jobs */
1282 if (outstanding > (int)app.client.stat_max_window)
1283 app.client.stat_max_window = outstanding;
1284
Benny Prijono514ca6b2006-07-03 01:30:01 +00001285 /* Wait if there are more pending jobs than allowed in the
1286 * window.
1287 */
Benny Prijono1479b652006-07-03 14:18:17 +00001288 for (i=0; outstanding > (int)app.client.job_window && i<100; ++i) {
Benny Prijono514ca6b2006-07-03 01:30:01 +00001289 pjsip_endpt_handle_events(app.sip_endpt, &timeout);
1290 outstanding = app.client.job_submitted - app.client.job_finished;
1291 }
1292
Benny Prijono1479b652006-07-03 14:18:17 +00001293
1294 /* Submit one job */
Benny Prijono514ca6b2006-07-03 01:30:01 +00001295 if (app.client.method.id == PJSIP_INVITE_METHOD) {
1296 status = make_call(&app.client.dst_uri);
1297 } else if (app.client.stateless) {
1298 status = submit_stateless_job();
1299 } else {
1300 status = submit_job();
1301 }
1302
1303 ++app.client.job_submitted;
1304
Benny Prijono1479b652006-07-03 14:18:17 +00001305 /* Handle event */
1306 pjsip_endpt_handle_events2(app.sip_endpt, &timeout, NULL);
1307
1308 /* Check for time out */
1309 if (app.client.job_submitted - last_timeout_check >= 2000) {
1310 pj_gettimeofday(&now);
1311 if (PJ_TIME_VAL_GTE(now, end_time))
Benny Prijono514ca6b2006-07-03 01:30:01 +00001312 break;
Benny Prijono1479b652006-07-03 14:18:17 +00001313 last_timeout_check = app.client.job_submitted;
Benny Prijono514ca6b2006-07-03 01:30:01 +00001314 }
1315 }
1316
Benny Prijonoc3573762006-07-10 21:39:24 +00001317 if (app.client.requests_sent.sec == 0) {
1318 pj_gettimeofday(&app.client.requests_sent);
1319 }
1320
1321
Benny Prijono514ca6b2006-07-03 01:30:01 +00001322 /* Wait until all jobs completes, or timed out */
Benny Prijonoc3573762006-07-10 21:39:24 +00001323 pj_gettimeofday(&now);
1324 while (now.sec < end_time.sec &&
1325 app.client.job_finished < app.client.job_count &&
1326 !app.thread_quit)
1327 {
1328 pj_time_val timeout = { 0, 1 };
Benny Prijono514ca6b2006-07-03 01:30:01 +00001329 unsigned i;
1330
Benny Prijonoc3573762006-07-10 21:39:24 +00001331 for (i=0; i<1000; ++i) {
Benny Prijono1479b652006-07-03 14:18:17 +00001332 pjsip_endpt_handle_events2(app.sip_endpt, &timeout, NULL);
Benny Prijono514ca6b2006-07-03 01:30:01 +00001333 }
1334
1335 pj_gettimeofday(&now);
Benny Prijonoc3573762006-07-10 21:39:24 +00001336 }
Benny Prijono514ca6b2006-07-03 01:30:01 +00001337
1338 return 0;
1339}
1340
1341
1342static const char *good_number(char *buf, pj_int32_t val)
1343{
1344 if (val < 1000) {
1345 pj_ansi_sprintf(buf, "%d", val);
1346 } else if (val < 1000000) {
1347 pj_ansi_sprintf(buf, "%d.%dK",
1348 val / 1000,
1349 (val % 1000) / 100);
1350 } else {
1351 pj_ansi_sprintf(buf, "%d.%02dM",
1352 val / 1000000,
1353 (val % 1000000) / 10000);
1354 }
1355
1356 return buf;
1357}
1358
1359
1360static int server_thread(void *arg)
1361{
Benny Prijonoc3573762006-07-10 21:39:24 +00001362 pj_time_val timeout = { 0, 1 };
Benny Prijono514ca6b2006-07-03 01:30:01 +00001363 unsigned thread_index = (unsigned)arg;
1364 pj_time_val last_report, next_report;
1365
1366 pj_gettimeofday(&last_report);
1367 next_report = last_report;
1368 next_report.sec++;
1369
1370 while (!app.thread_quit) {
1371 pj_time_val now;
1372 unsigned i;
1373
1374 for (i=0; i<100; ++i) {
1375 unsigned count = 0;
1376 pjsip_endpt_handle_events2(app.sip_endpt, &timeout, &count);
1377 if (count == 0)
1378 break;
1379 }
1380
1381 if (thread_index == 0) {
1382 pj_gettimeofday(&now);
1383
1384 if (PJ_TIME_VAL_GTE(now, next_report)) {
1385 pj_time_val tmp;
1386 unsigned msec;
1387 unsigned stateless, stateful, call;
1388 char str_stateless[32], str_stateful[32], str_call[32];
1389
1390 tmp = now;
1391 PJ_TIME_VAL_SUB(tmp, last_report);
1392 msec = PJ_TIME_VAL_MSEC(tmp);
1393
1394 last_report = now;
1395 next_report = last_report;
1396 next_report.sec++;
1397
1398 stateless = app.server.cur_state.stateless_cnt - app.server.prev_state.stateless_cnt;
1399 stateful = app.server.cur_state.stateful_cnt - app.server.prev_state.stateful_cnt;
1400 call = app.server.cur_state.call_cnt - app.server.prev_state.call_cnt;
1401
1402 good_number(str_stateless, app.server.cur_state.stateless_cnt);
1403 good_number(str_stateful, app.server.cur_state.stateful_cnt);
1404 good_number(str_call, app.server.cur_state.call_cnt);
1405
Benny Prijono1479b652006-07-03 14:18:17 +00001406 printf("Total(rate): stateless:%s (%d/s), statefull:%s (%d/s), call:%s (%d/s) \r",
Benny Prijono514ca6b2006-07-03 01:30:01 +00001407 str_stateless, stateless*1000/msec,
1408 str_stateful, stateful*1000/msec,
1409 str_call, call*1000/msec);
1410 fflush(stdout);
1411
1412 app.server.prev_state = app.server.cur_state;
1413 }
1414 }
1415 }
1416
1417 return 0;
1418}
1419
Benny Prijono1479b652006-07-03 14:18:17 +00001420static void write_report(const char *msg)
1421{
1422 puts(msg);
1423
1424#if defined(PJ_WIN32) && PJ_WIN32!=0
1425 OutputDebugString(msg);
1426 OutputDebugString("\n");
1427#endif
1428}
1429
1430
Benny Prijono514ca6b2006-07-03 01:30:01 +00001431int main(int argc, char *argv[])
1432{
Benny Prijono1479b652006-07-03 14:18:17 +00001433 static char report[1024];
Benny Prijono514ca6b2006-07-03 01:30:01 +00001434
1435 if (create_app() != 0)
1436 return 1;
1437
1438 if (init_options(argc, argv) != 0)
1439 return 1;
1440
1441 if (init_sip() != 0)
1442 return 1;
1443
1444 if (init_media() != 0)
1445 return 1;
1446
Benny Prijonoc3573762006-07-10 21:39:24 +00001447 pj_log_set_level(app.log_level);
1448
1449 if (app.log_level > 3) {
Benny Prijono514ca6b2006-07-03 01:30:01 +00001450 pjsip_endpt_register_module(app.sip_endpt, &msg_logger);
1451 }
1452
1453
1454 /* Misc infos */
1455 if (app.client.dst_uri.slen != 0) {
Benny Prijonoc3573762006-07-10 21:39:24 +00001456 if (app.client.method.id == PJSIP_INVITE_METHOD) {
1457 if (app.client.stateless) {
1458 PJ_LOG(3,(THIS_FILE,
1459 "Info: --stateless option makes no sense for INVITE,"
1460 " ignored."));
1461 }
Benny Prijono514ca6b2006-07-03 01:30:01 +00001462 }
Benny Prijonoc3573762006-07-10 21:39:24 +00001463
Benny Prijono514ca6b2006-07-03 01:30:01 +00001464 }
1465
Benny Prijono514ca6b2006-07-03 01:30:01 +00001466
1467
1468 if (app.client.dst_uri.slen) {
1469 /* Client mode */
1470 pj_status_t status;
1471 char test_type[64];
Benny Prijonoc3573762006-07-10 21:39:24 +00001472 unsigned msec_req, msec_res;
Benny Prijono514ca6b2006-07-03 01:30:01 +00001473 unsigned i;
1474
1475 /* Get the job name */
1476 if (app.client.method.id == PJSIP_INVITE_METHOD) {
1477 pj_ansi_strcpy(test_type, "INVITE calls");
1478 } else if (app.client.stateless) {
1479 pj_ansi_sprintf(test_type, "stateless %.*s requests",
1480 (int)app.client.method.name.slen,
1481 app.client.method.name.ptr);
1482 } else {
1483 pj_ansi_sprintf(test_type, "stateful %.*s requests",
1484 (int)app.client.method.name.slen,
1485 app.client.method.name.ptr);
1486 }
1487
1488
Benny Prijonoc3573762006-07-10 21:39:24 +00001489 PJ_LOG(3,(THIS_FILE, "Sending %d %s to '%.*s' with %d maximum outstanding jobs, please wait..",
Benny Prijonoc03a3c52006-07-03 02:31:10 +00001490 app.client.job_count, test_type,
Benny Prijonoc3573762006-07-10 21:39:24 +00001491 (int)app.client.dst_uri.slen, app.client.dst_uri.ptr,
1492 app.client.job_window));
Benny Prijono514ca6b2006-07-03 01:30:01 +00001493
1494 for (i=0; i<app.thread_count; ++i) {
1495 status = pj_thread_create(app.pool, NULL, &client_thread, NULL,
1496 0, 0, &app.thread[i]);
1497 if (status != PJ_SUCCESS) {
1498 app_perror(THIS_FILE, "Unable to create thread", status);
1499 return 1;
1500 }
1501 }
1502
1503 for (i=0; i<app.thread_count; ++i) {
1504 pj_thread_join(app.thread[i]);
1505 app.thread[i] = NULL;
1506 }
1507
1508 if (app.client.last_completion.sec) {
1509 pj_time_val duration;
1510 duration = app.client.last_completion;
1511 PJ_TIME_VAL_SUB(duration, app.client.first_request);
Benny Prijonoc3573762006-07-10 21:39:24 +00001512 msec_res = PJ_TIME_VAL_MSEC(duration);
Benny Prijono514ca6b2006-07-03 01:30:01 +00001513 } else {
Benny Prijonoc3573762006-07-10 21:39:24 +00001514 msec_res = app.client.timeout * 1000;
Benny Prijono514ca6b2006-07-03 01:30:01 +00001515 }
1516
Benny Prijonoc3573762006-07-10 21:39:24 +00001517 if (msec_res == 0) msec_res = 1;
1518
1519 if (app.client.requests_sent.sec) {
1520 pj_time_val duration;
1521 duration = app.client.requests_sent;
1522 PJ_TIME_VAL_SUB(duration, app.client.first_request);
1523 msec_req = PJ_TIME_VAL_MSEC(duration);
1524 } else {
1525 msec_req = app.client.timeout * 1000;
1526 }
1527
1528 if (msec_req == 0) msec_req = 1;
1529
Benny Prijono514ca6b2006-07-03 01:30:01 +00001530
Benny Prijono1479b652006-07-03 14:18:17 +00001531 pj_ansi_snprintf(
1532 report, sizeof(report),
Benny Prijonoc3573762006-07-10 21:39:24 +00001533 "Total %d %s sent in %d ms at rate %d/sec\n"
1534 "Total %d responses receieved in %d ms:\n"
Benny Prijono1479b652006-07-03 14:18:17 +00001535 " - 2xx responses: %7d (rate=%d/sec)\n"
1536 " - 3xx responses: %7d (rate=%d/sec)\n"
1537 " - 4xx responses: %7d (rate=%d/sec)\n"
1538 " - 5xx responses: %7d (rate=%d/sec)\n"
1539 " - 6xx responses: %7d (rate=%d/sec)\n"
1540 " - 7xx responses: %7d (rate=%d/sec)\n"
1541 " ----------------\n"
1542 " TOTAL responses: %7d (rate=%d/sec)\n",
Benny Prijonoc3573762006-07-10 21:39:24 +00001543 app.client.job_submitted, test_type, msec_req,
1544 app.client.job_submitted * 1000 / msec_req,
1545 app.client.total_responses, msec_res,
1546 app.client.status_class[2], app.client.status_class[2]*1000/msec_res,
1547 app.client.status_class[3], app.client.status_class[3]*1000/msec_res,
1548 app.client.status_class[4], app.client.status_class[4]*1000/msec_res,
1549 app.client.status_class[5], app.client.status_class[5]*1000/msec_res,
1550 app.client.status_class[6], app.client.status_class[6]*1000/msec_res,
1551 app.client.status_class[7], app.client.status_class[7]*1000/msec_res,
1552 app.client.total_responses, app.client.total_responses*1000/msec_res);
Benny Prijono1479b652006-07-03 14:18:17 +00001553
1554 write_report(report);
1555
1556 pj_ansi_sprintf(report, "Maximum outstanding job: %d",
1557 app.client.stat_max_window);
1558 write_report(report);
1559
Benny Prijono514ca6b2006-07-03 01:30:01 +00001560
1561 } else {
1562 /* Server mode */
1563 char s[10];
1564 pj_status_t status;
1565 unsigned i;
1566
1567 puts("");
1568 puts("pjsip-perf started in server-mode");
1569
1570 printf("Receiving requests on the following URIs:\n"
Benny Prijonoc03a3c52006-07-03 02:31:10 +00001571 " sip:0@%.*s:%d;transport=%s for stateless handling (non-INVITE only)\n"
1572 " sip:1@%.*s:%d;transport=%s for stateful handling (INVITE and non-INVITE)\n"
1573 " sip:2@%.*s:%d;transport=%s for call handling (INVITE only)\n",
Benny Prijono514ca6b2006-07-03 01:30:01 +00001574 (int)app.local_addr.slen,
1575 app.local_addr.ptr,
1576 app.local_port,
Benny Prijonoc03a3c52006-07-03 02:31:10 +00001577 (app.use_tcp ? "tcp" : "udp"),
Benny Prijono514ca6b2006-07-03 01:30:01 +00001578 (int)app.local_addr.slen,
1579 app.local_addr.ptr,
1580 app.local_port,
Benny Prijonoc03a3c52006-07-03 02:31:10 +00001581 (app.use_tcp ? "tcp" : "udp"),
Benny Prijono514ca6b2006-07-03 01:30:01 +00001582 (int)app.local_addr.slen,
1583 app.local_addr.ptr,
Benny Prijonoc03a3c52006-07-03 02:31:10 +00001584 app.local_port,
1585 (app.use_tcp ? "tcp" : "udp"));
Benny Prijono514ca6b2006-07-03 01:30:01 +00001586
1587 for (i=0; i<app.thread_count; ++i) {
1588 status = pj_thread_create(app.pool, NULL, &server_thread, (void*)i,
1589 0, 0, &app.thread[i]);
1590 if (status != PJ_SUCCESS) {
1591 app_perror(THIS_FILE, "Unable to create thread", status);
1592 return 1;
1593 }
1594 }
1595
1596 puts("Press <ENTER> to quit");
1597 fflush(stdout);
1598 fgets(s, sizeof(s), stdin);
1599
1600 app.thread_quit = PJ_TRUE;
1601 for (i=0; i<app.thread_count; ++i) {
1602 pj_thread_join(app.thread[i]);
1603 app.thread[i] = NULL;
1604 }
1605 }
1606
1607
1608 destroy_app();
1609
1610 return 0;
1611}
1612