1    	/*
2    	 * Copyright (c) 2011 Red Hat, Inc.
3    	 *
4    	 * All rights reserved.
5    	 *
6    	 * Author: Angus Salkeld <asalkeld@redhat.com>
7    	 *
8    	 * libqb is free software: you can redistribute it and/or modify
9    	 * it under the terms of the GNU Lesser General Public License as published by
10   	 * the Free Software Foundation, either version 2.1 of the License, or
11   	 * (at your option) any later version.
12   	 *
13   	 * libqb is distributed in the hope that it will be useful,
14   	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15   	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   	 * GNU Lesser General Public License for more details.
17   	 *
18   	 * You should have received a copy of the GNU Lesser General Public License
19   	 * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
20   	 */
21   	#include "os_base.h"
22   	#include <signal.h>
23   	
24   	#include <qb/qbdefs.h>
25   	#include <qb/qbutil.h>
26   	#include <qb/qbipcc.h>
27   	#include <qb/qblog.h>
28   	
29   	static int32_t do_benchmark = QB_FALSE;
30   	static int32_t use_events = QB_FALSE;
31   	static int alarm_notice;
32   	static qb_util_stopwatch_t *sw;
33   	#define ONE_MEG 1048576
34   	static char *data;
35   	
36   	struct my_req {
37   		struct qb_ipc_request_header hdr;
38   		char message[256];
39   	};
40   	
41   	struct my_res {
42   		struct qb_ipc_response_header hdr;
43   		char message[256];
44   	};
45   	
46   	static void sigalrm_handler (int num)
47   	{
48   		alarm_notice = 1;
49   	}
50   	
51   	static void
52   	_benchmark(qb_ipcc_connection_t *conn, int write_size)
53   	{
54   		struct iovec iov[2];
55   		ssize_t res;
56   		struct qb_ipc_request_header hdr;
57   		int write_count = 0;
58   		float secs;
59   	
60   		alarm_notice = 0;
61   		hdr.size = write_size;
62   		hdr.id = QB_IPC_MSG_USER_START + 1;
63   	
64   		iov[0].iov_base = (void*)&hdr;
65   		iov[0].iov_len = sizeof(struct qb_ipc_request_header);
66   	
67   		iov[1].iov_base = data;
68   		iov[1].iov_len = write_size - sizeof(struct qb_ipc_request_header);
69   	
70   		alarm (10);
71   	
72   		qb_util_stopwatch_start(sw);
73   		do {
74   			res = qb_ipcc_sendv(conn, iov, 2);
(1) Event cond_false: Condition "res == write_size", taking false branch.
75   			if (res == write_size) {
76   				write_count++;
(2) Event if_end: End of if statement.
77   			}
(3) Event cond_true: Condition "alarm_notice == 0", taking true branch.
(4) Event cond_false: Condition "res == write_size", taking false branch.
(5) Event cond_false: Condition "res == -11", taking false branch.
78   		} while (alarm_notice == 0 && (res == write_size || res == -EAGAIN));
(6) Event cond_true: Condition "res < 0", taking true branch.
79   		if (res < 0) {
80   			perror("qb_ipcc_sendv");
81   		}
82   		qb_util_stopwatch_stop(sw);
(7) Event zero_return: Function call "qb_util_stopwatch_sec_elapsed_get(sw)" returns 0.0. [details]
(8) Event assign: Assigning: "secs" = "qb_util_stopwatch_sec_elapsed_get(sw)".
Also see events: [divide_by_zero]
83   		secs = qb_util_stopwatch_sec_elapsed_get(sw);
84   	
85   		printf ("%5d messages sent ", write_count);
86   		printf ("%5d bytes per write ", write_size);
87   		printf ("%7.3f Seconds runtime ", secs);
(9) Event divide_by_zero: In expression "(float)write_count / secs", division by expression "secs" which may be zero results in either +infinity, -infinity, or NaN.
Also see events: [zero_return][assign]
88   		printf ("%9.3f TP/s ",
89   			((float)write_count) / secs);
90   		printf ("%7.3f MB/s.\n",
91   			((float)write_count) * ((float)write_size) / secs);
92   	}
93   	
94   	
95   	static void
96   	do_throughput_benchmark(qb_ipcc_connection_t *conn)
97   	{
98   		ssize_t size = 64;
99   		int i;
100  	
101  		signal (SIGALRM, sigalrm_handler);
102  		sw =  qb_util_stopwatch_create();
103  	
104  		for (i = 0; i < 10; i++) { /* number of repetitions - up to 50k */
105  			_benchmark (conn, size);
106  			signal (SIGALRM, sigalrm_handler);
107  			size *= 5;
108  			if (size >= (ONE_MEG - 100)) {
109  				break;
110  			}
111  		}
112  	}
113  	
114  	static void
115  	do_echo(qb_ipcc_connection_t *conn)
116  	{
117  		struct my_req req;
118  		struct my_res res;
119  		char *newline;
120  		int32_t rc;
121  		int32_t send_ten_events;
122  	
123  		while (1) {
124  			printf("SEND (q or Q to quit) : ");
125  			if (fgets(req.message, 256, stdin) == NULL) {
126  				continue;
127  			}
128  			newline = strrchr(req.message, '\n');
129  			if (newline) {
130  				*newline = '\0';
131  			}
132  	
133  			if (strcasecmp(req.message, "q") == 0) {
134  				break;
135  			} else {
136  				req.hdr.id = QB_IPC_MSG_USER_START + 3;
137  				req.hdr.size = sizeof(struct my_req);
138  				rc = qb_ipcc_send(conn, &req, req.hdr.size);
139  				if (rc < 0) {
140  					perror("qb_ipcc_send");
141  					exit(0);
142  				}
143  			}
144  	
145  			send_ten_events = (strcasecmp(req.message, "events") == 0);
146  	
147  			if (rc > 0) {
148  				if (use_events && !send_ten_events) {
149  					printf("waiting for event recv\n");
150  					rc = qb_ipcc_event_recv(conn, &res, sizeof(res), -1);
151  				} else {
152  					printf("waiting for recv\n");
153  					rc = qb_ipcc_recv(conn, &res, sizeof(res), -1);
154  				}
155  				printf("recv %d\n", rc);
156  				if (rc < 0) {
157  					perror("qb_ipcc_recv");
158  					exit(0);
159  				}
160  				if (send_ten_events) {
161  					int32_t i;
162  					printf("waiting for 10 events\n");
163  					for (i = 0; i < 10; i++) {
164  						rc = qb_ipcc_event_recv(conn, &res, sizeof(res), -1);
165  						if (rc < 0) {
166  							perror("qb_ipcc_event_recv");
167  						} else {
168  							printf("got event %d rc:%d\n", i, rc);
169  						}
170  					}
171  				}
172  				printf("Response[%d]: %s \n", res.hdr.id, res.message);
173  			}
174  		}
175  	}
176  	
177  	static void
178  	show_usage(const char *name)
179  	{
180  		printf("usage: \n");
181  		printf("%s <options>\n", name);
182  		printf("\n");
183  		printf("  options:\n");
184  		printf("\n");
185  		printf("  -h             show this help text\n");
186  		printf("  -b             benchmark\n");
187  		printf("  -e             use events instead of responses\n");
188  		printf("\n");
189  	}
190  	
191  	int
192  	main(int argc, char *argv[])
193  	{
194  		qb_ipcc_connection_t *conn;
195  		const char *options = "ebh";
196  		int32_t opt;
197  	
198  		while ((opt = getopt(argc, argv, options)) != -1) {
199  			switch (opt) {
200  			case 'b':
201  				do_benchmark = QB_TRUE;
202  				break;
203  			case 'e':
204  				use_events = QB_TRUE;
205  				break;
206  			case 'h':
207  			default:
208  				show_usage(argv[0]);
209  				exit(0);
210  				break;
211  			}
212  		}
213  	
214  	
215  		qb_log_init("ipcclient", LOG_USER, LOG_TRACE);
216  		qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
217  		qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
218  				  QB_LOG_FILTER_FILE, "*", LOG_TRACE);
219  		qb_log_format_set(QB_LOG_STDERR, "%f:%l [%p] %b");
220  		qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
221  	
222  		/* Our example server is enforcing a buffer size minimum,
223  		 * so the client does not need to be concerned with setting
224  		 * the buffer size */
225  		conn = qb_ipcc_connect("ipcserver", 0);
226  		if (conn == NULL) {
227  			perror("qb_ipcc_connect");
228  			exit(1);
229  		}
230  		data = calloc(1, qb_ipcc_get_buffer_size(conn));
231  	
232  		if (do_benchmark) {
233  			do_throughput_benchmark(conn);
234  		} else {
235  			do_echo(conn);
236  		}
237  	
238  		qb_ipcc_disconnect(conn);
239  		free(data);
240  		qb_log_fini();
241  		return EXIT_SUCCESS;
242  	}
243