]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - ctprof_srv/ctprof_srv.git/blob - server/ctoolsprof_srv_main.c
Version 1.1 Update
[ctprof_srv/ctprof_srv.git] / server / ctoolsprof_srv_main.c
1 /*
2  * ctoolsprof_srv_main.c
3  *
4  * Ctools Profiler Server Implementation
5  *
6  * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ 
7  * 
8  * 
9  *  Redistribution and use in source and binary forms, with or without 
10  *  modification, are permitted provided that the following conditions 
11  *  are met:
12  *
13  *    Redistributions of source code must retain the above copyright 
14  *    notice, this list of conditions and the following disclaimer.
15  *
16  *    Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the 
18  *    documentation and/or other materials provided with the   
19  *    distribution.
20  *
21  *    Neither the name of Texas Instruments Incorporated nor the names of
22  *    its contributors may be used to endorse or promote products derived
23  *    from this software without specific prior written permission.
24  *
25  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
26  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
27  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28  *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
29  *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
30  *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
31  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
34  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
35  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36  *
37 */
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <stdbool.h>
41 #include <string.h>
42 #include <getopt.h>
43 #include <stdint.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <fcntl.h>
47 #include <unistd.h>
48 #include <errno.h>
49 #include "error_handler.h"
50 #include "logging.h"
51 #include "socket.h"
52 #include "command_handler.h"
53 #include "remote_commands.h"
54 #include "signal_handler.h"
55 #include "etb_handler.h"
56 #include "ctoolsprof.h"
58 const int g_major_version = 1;
59 const int g_minor_version = 1;
60 const int g_copyright_year = 2013;
62 FILE *g_stdout;
63 FILE *g_stderr;
64 char * g_whoami;
66 /* This is used by error_handler.c to determine if an error causes
67  * program exit or decides based on if the error is fatal or not.
68  */
69 bool g_interactive_mode = false;
71 /* This is used to enable sending the server state over a named pipe */
72 bool g_fifo_used = false;
73 bool g_fifo_enabled = false;
74 int g_fifo_fd = -1;
75 static char * fifo_filename = "ctprof_fifo";
77 /* g_terminate is used to terminate the server on a disconnect from the client.
78  * AND it will not allow g_fifo_enabled to be set false (which causes
79  * the server to block trying to open the pipe again).
80  */
81 bool g_terminate = false;
82 static const char msg_terminate[] = "client terminate\n";
84 static int cmd_port = 54242;
85 static int etb_port = 54243;
86 //static int log_port = 54244;
88 /* Global server state initialization*/
89 monitor_state_t global_state = {
90     .delay_active = false,
91     .duration_active = false,
92     .signal_active = false,
93     .etb_active = false,
94     .socket_disconnect = true,
95     .etb_mode = 0,
96     .recording = STATE_UNINITIALIZED
97 };
99 static int cmd_socket;
100 static int etb_socket;
103 /***************************************************************************** 
104  *  Private functions
105  *****************************************************************************/
107 /***************************************************************************** 
108  *  socket_connect()
109  *
110  *  This function called by main to open the command and etb sockets with the
111  *  client (running on the host).
112  *
113  *  - Initialize the socket tables.
114  *  - Create command and etb ports. This binds, listens for the client and then
115  *    accepts the 
116  *  - Make each port non-blocking and register with their handler functions.
117  *  - Set the global socket disconnect state to false.
118  *  
119  *****************************************************************************/
121 static void socket_connect(void)
123     LOGFUNC();
125     /* Initialize the socket tables */
126     socket_open();
127     fprintf(g_stdout, "%s:Waiting for client to connect\n", g_whoami);
129     /* Configure a socket port and wait until client connects */   
130     cmd_socket = socket_server_create(cmd_port);
131     fprintf(g_stdout, "%s:Client command port connected\n", g_whoami);
133     /* Optimize the command socket for server operation. */
134     socket_command_optimize(cmd_socket);
136     etb_socket = socket_server_create(etb_port);
137     fprintf(g_stdout, "%s:Client etb service port connected\n", g_whoami);
139     
140     /* Make the command socket non-blocking so we don't 
141      * wait on writes to complete. Add command socket to
142      * list of read file descriptors to socket_wait() to wait on.
143      */
144     socket_set_nonblocking(cmd_socket);
145     bool is_read_socket = true;
146     socket_add_list(cmd_socket, is_read_socket);
148     /* Register the command socket with the command handler */
149     command_init(cmd_socket);
151     /* Make the etb socket non-blocking and register the 
152      * etb socket with the etb_handler.
153      * Note - unlike the command socket, no need to add
154      * the etb socket to the list of sockets to check by 
155      * socket_wait() until there is some ETB data available.
156      */
157     socket_set_nonblocking(etb_socket);
158     etb_init(etb_socket); 
160     global_state.socket_disconnect = false;
164 /***************************************************************************** 
165  *  clean_up_socket()
166  *
167  *  This function called by main when main detects a socket disconnect. Normally
168  *  a socket disconnect only occurs when the client terminates.
169  *  
170  *****************************************************************************/
171 static void clean_up_socket(void)
174     LOGFUNC();
176     socket_remove_list(cmd_socket);
177     socket_remove_list(etb_socket);
178     socket_close(cmd_socket);
179     socket_close(etb_socket);
183 /***************************************************************************** 
184  *  Public functions
185  *****************************************************************************/
187 /***************************************************************************** 
188  *  main()
189  *
190  *  Main evaluates a set of simple command line parameters and then drops into
191  *  an endless loop that processes client commands or returns trace data 
192  *  (if available).  
193  * 
194  * TODO:
195  * - Add command line option to send server logging data back to the client.
196  *****************************************************************************/
197  
198 int main(int argc, char *argv[])
200     /* Initialize globals */
201     g_stdout = stdout;
202     g_stderr = stderr;
203     g_whoami = argv[0];
205     signal_handler_init(start_recording, stop_recording);
207     /* evaluate command line */
208     while (1) {
209         struct option long_options[] = {
210             {"port", required_argument, 0, 'p'},
211             {"pipe", no_argument, 0,'P'},
212             {"quiet", no_argument, 0,'q'},
213             {"help", no_argument, 0, 'h'},
214             {"logging", required_argument, 0, 'l'},
215             {"version", no_argument, 0, 'v'},
216             {"terminate", no_argument, 0, 't'}
217         };
219         char * short_options = "p:qhl:vPt";
220         int option, option_index = 0;
222         option = getopt_long(argc, argv, short_options, long_options, &option_index);
223         
224         /* No more options then all done */
225         if (option == -1) break;
227         switch (option) {
228         case 'P':
229         {
230             struct stat fifo_stat;
231             if (stat(fifo_filename, &fifo_stat) == 0) {
232                 if(!S_ISFIFO(fifo_stat.st_mode)) {
233                     fprintf(g_stderr, "%s:%s already exists but is not a fifo",
234                             g_whoami, fifo_filename);
235                     err_handler(ERR_TYPE_SYSTEM, ERR_FIFO_MAKE);                   
236                 }
237             } else {
238                 if (mkfifo(fifo_filename, 0666) == -1) {
239                     fprintf(g_stderr, "%s:Can't make %s fifo file",
240                             g_whoami, fifo_filename);
241                     err_handler(ERR_TYPE_SYSTEM, ERR_FIFO_MAKE);
242                 }
243             }
245             g_fifo_used = true;
246             g_fifo_enabled = false; 
247             break;
248         }
249         case 'p':
250             cmd_port = atoi(optarg);
251             etb_port = cmd_port + 1;
252 //          log_port = cmd_port + 2;
253             break;
254         case 'q':
255             g_stdout = fopen("/dev/null", "w");
256             break;
257         case '?': /* getopt did not understand the option */
258         case 'h':
259             fprintf(g_stdout, "Usage: %s [hlpPqv]\n", g_whoami);
260             fprintf(g_stdout, " --help/-h               Print this\n");
261             fprintf(g_stdout, " --logging/-l <level>    Enable logging to LOG_FILENAME\n");
262             fprintf(g_stdout, "                         Level 0 - User information logging\n");
263             fprintf(g_stdout, "                         Level 1 - Debug logging\n");
264             fprintf(g_stdout, " --port/-p <ip port>     Set port value\n");
265             fprintf(g_stdout, " --pipe/-P               Issue server recording state over a named pipe\n");
266             fprintf(g_stdout, " --quiet/-q              Send stdout to /dev/null\n");
267             fprintf(g_stdout, " --terminate/-t          Terminate server on disconnect from client\n");
268             fprintf(g_stdout, " --version/-v            Print version\n");
269             fprintf(g_stdout, "\n");
270             exit(0);
271         case 'l':
272         {
273             /* Yes, this looks a bit strange, but in order to use the same
274              * log_handler for both server and client, must conform to argtable convention
275              */
276             int level[1] = {atoi(optarg)};
277             log_handler((void **)&level, 1);
278             if (level[1] == 0)
279                 fprintf(g_stdout, "%s:Logging level set to user info logging\n",
280                         g_whoami);
281             else
282                 fprintf(g_stdout, "%s:Logging level set to debug logging\n",
283                         g_whoami);
284             break;
285         }
286         case 't':
287             g_terminate = true;
288             break;
289         case 'v':
290             fprintf(g_stdout, "%s:version %d.%d\n", g_whoami, g_major_version,
291                     g_minor_version);
292             fprintf(g_stdout, "%s:Copyright (C) %d Texas Instruments, Inc.\n", 
293                     g_whoami, g_copyright_year); 
294             exit(0);
295             break;
296         default:
297             err_handler(ERR_TYPE_LOCAL, ERR_PARSER);
298         } /* End of switch */
300     }
302     socket_connect();
304     /* This loop is the heart of the server! */
305     do {
307         /* If disconnected then try to reconnect - will stall here until a 
308          * client connects.
309          */
310         if (global_state.socket_disconnect) {
311             LOGMSG1("%s:socket disconnect detected. listening for client connection.", __func__);
313             if (g_terminate) {
314                 if (g_fifo_enabled) {            
315                     char * broken_pipe_msg = NULL;
316                     remote_pipe_write(msg_terminate, strlen(msg_terminate), broken_pipe_msg);
318                     LOGMSG1("%s:write to the pipe %s", __func__, msg_terminate);
319                 }
321                 clean_up();
322                 exit(0);
323             } 
325             socket_connect();
326         }
328         /* Open the fifo if required */
329         if ((g_fifo_used == true) && (g_fifo_fd == -1)) {
331             fprintf(g_stdout,"%s:Opening pipe for writing. " 
332                              "Waiting on pipe to be open for reading.\n",
333                              g_whoami);
335             LOGMSG1("%s:Opening %s for writing.", __func__, fifo_filename);
337             if ((g_fifo_fd = open(fifo_filename, O_WRONLY)) == -1) {
338                 fprintf(g_stderr, "%s:Can't open %s fifo file", g_whoami,
339                         fifo_filename );
340                 err_handler(ERR_TYPE_SYSTEM, ERR_FIFO_OPEN);
341             }
343             fprintf(g_stdout,"%s:pipe open for reading\n", g_whoami);
344             LOGMSG1("%s:%s open for reading", __func__, fifo_filename);
345         }
347         /* If the fifo is used (set by command line -P option) but it is not
348          * enabled, time to enable it and send the first message (the server's pid).
349          */
350         if ((g_fifo_used == true) && (g_fifo_enabled == false)) {         
351             g_fifo_enabled = true;
353             /* First message written is this task's pid */
354             pid_t mypid = getpid();
356             char * broken_pipe_msg = "ctprof_srv's pid";
357             remote_pipe_write((char *)&mypid, sizeof(pid_t), broken_pipe_msg);
359             LOGMSG1("%s:write to the pipe %d", __func__, mypid);
361             LOGMSG1("%s:wrote ctprof_srv pid %d to %s", __func__, mypid, fifo_filename);           
362         }
364 #if DEBUG
365         int state = 0;
366         fprintf (g_stdout,"Processing ...|");
367 #endif
368         /* This is the processing loop - socket_wait will wait for:
369          * - A command socket message
370          * - An empty etb socket that is ready for more trace data
371          * - Or a timeout (socket_wait() returns 0 on a timeout)      
372          */ 
374         while (!socket_wait()) {
375             /* socket_wait() timeout - go check for available trace data */
376 #if DEBUG
377             state++;
378             if (state == 1) fprintf (g_stdout, "\b/");
379             else if (state == 2) fprintf (g_stdout, "\b-");
380             else if (state == 3) fprintf (g_stdout, "\b\\"); 
381             else if (state == 4) {fprintf (g_stdout, "\b|"); state = 0;}
382             fflush(stdout);
383 #endif
384             /* Don't want signals messing with our etb processing */
385             signal_handler_block();
387             if ((global_state.recording == STATE_RECORDING) 
388                 && (global_state.etb_active == true) 
389                 && (global_state.etb_mode == ETB_MODE_DRAIN)) {
390                
391                 etb_add_queue(ETB_QUEUE_LIMIT);
392                                   
393             }
395             signal_handler_unblock();
396             
397         }
398         /* socket_wait() returned non-zero which means there is either a
399          * command message ready to service or the etb socket is ready
400          * for more data.
401          */
402 #if DEBUG
403         fprintf(g_stdout, "\r");
404 #endif
405         /* Don't want signals messing with our command or etb processing. */
406         signal_handler_block();
408         /* If the command socket is on the read ready list, a command
409          * message is ready to process.
410          */
411         if (socket_check_read_list(cmd_socket)) {
412             command_process();
414         }
416         /* If the etb socket is on the write ready list, then more
417          * etb data can be sent to the etb socket.
418          */
419         if (socket_check_write_list(etb_socket)) {
420             etb_send_from_queue();
421         }
423         signal_handler_unblock();
425         /* Check for a client disconnect. This can only happen from 
426          * socket_recv_data() (see socket.c).
427          */
428         if (global_state.socket_disconnect) {
429             clean_up_socket();
430         }
432     } while (1);
433     
434     socket_remove_list(cmd_socket);
435     socket_close(cmd_socket);
436  
439 /***************************************************************************** 
440  *  clean_up()
441  *
442  *  This function is called to clean up any resources being used. It is called
443  *  only by the error_handler.c when a fatal error is encountered, or when any
444  *  error is encountered if not in interactive mode.  
445  * 
446  *****************************************************************************/
447 void clean_up(void)
449     LOGFUNC();
451     clean_up_socket();
453     if (g_fifo_enabled) {
454         close(g_fifo_fd);
455     }
457     if (g_fifo_used) {
458         unlink(fifo_filename);
459     }