]> git.cworth.org Git - vogl/blob - src/voglcmd/trace.cpp
Initial vogl checkin
[vogl] / src / voglcmd / trace.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <time.h>
31 #include <sys/time.h>
32 #include <errno.h>
33 #include <signal.h>
34
35 #include <vogl_core.h>
36 #include <vogl_json.h>
37
38 #include "../common/SimpleOpt.h"
39 #include "../common/channel.h"
40
41 #include "../common/commands.h"
42 #include "../common/launchsteamgame.h"
43 #include "../common/toclientmsg.h"
44 #include "../common/pinggame.h"
45 #include "../common/listfiles.h"
46
47 enum
48 {
49     OPT_HELP = 0,
50     OPT_SERVER,
51     OPT_PORT,
52     OPT_GAMEID,
53     OPT_BITNESS,
54     OPT_MONITOR,
55     OPT_CAPTURE,
56     OPT_RETRIEVE,
57     OPT_LISTGAMES,
58     OPT_LISTTRACES,
59     OPT_MAX
60 };
61
62
63 CSimpleOpt::SOption g_rgOptions[] =
64     {
65         { OPT_HELP,     "-?",         SO_NONE },
66         { OPT_HELP,     "-h",         SO_NONE },
67         { OPT_HELP,     "-help",      SO_NONE },
68         { OPT_HELP,     "--help",     SO_NONE },
69
70         { OPT_PORT,     "-p",         SO_REQ_SEP },
71         { OPT_PORT,     "-port",      SO_REQ_SEP },
72         { OPT_PORT,     "--port",     SO_REQ_SEP },
73
74         { OPT_SERVER,   "-s",         SO_REQ_SEP },
75         { OPT_SERVER,   "-server",    SO_REQ_SEP },
76         { OPT_SERVER,   "--server",   SO_REQ_SEP },
77
78         { OPT_GAMEID,   "--game",     SO_REQ_SEP },
79         { OPT_GAMEID,   "-game",      SO_REQ_SEP },
80         { OPT_GAMEID,   "-g",         SO_REQ_SEP },
81
82         { OPT_LISTGAMES,   "--listgames",     SO_NONE },
83         { OPT_LISTGAMES,   "-listgames",      SO_NONE },
84         { OPT_LISTGAMES,   "-lg",             SO_NONE },
85
86         { OPT_BITNESS,  "--bitness",  SO_REQ_SEP },
87         { OPT_BITNESS,  "-bitness",   SO_REQ_SEP },
88         { OPT_BITNESS,  "-b",         SO_REQ_SEP },
89
90         { OPT_MONITOR,  "--monitor",  SO_NONE },
91         { OPT_MONITOR,  "-monitor",   SO_NONE },
92         { OPT_MONITOR,  "-m",         SO_NONE },
93
94         { OPT_CAPTURE,  "--capture",  SO_REQ_SEP },
95         { OPT_CAPTURE,  "-capture",   SO_REQ_SEP },
96         { OPT_CAPTURE,  "-c",         SO_REQ_SEP },
97
98         { OPT_RETRIEVE, "--retrieve", SO_REQ_SEP },
99         { OPT_RETRIEVE, "-retrieve",  SO_REQ_SEP },
100         { OPT_RETRIEVE, "-r",         SO_REQ_SEP },
101
102         { OPT_LISTTRACES,   "--listtraces",     SO_NONE },
103         { OPT_LISTTRACES,   "-listtraces",      SO_NONE },
104         { OPT_LISTTRACES,   "-lt",             SO_NONE },
105
106         SO_END_OF_OPTIONS
107     };
108
109 void ShowUsage(char *szAppName);
110 static const char *GetLastErrorText(int a_nError);
111
112 void ctrlc_handler(int s) __attribute__ ((noreturn));
113
114 char g_localhost[] = "localhost";
115 bool g_fMonitor = false;
116 network::channelmgr *g_pChannelMgr = NULL;
117
118 void ProcessCommand(void *callbackParam, unsigned int cbData, char *pbData);
119
120 int
121 main_trace(int argc, char *argv[])
122 {
123     char *szServer = g_localhost;
124     struct sigaction sigIntHandler;
125     int port = DEFAULT_PORT;
126     network::CHEC ec = network::EC_NONE;
127     bool fListTraces = false;
128
129     char *gameid = NULL;
130     int bitness = 32;
131
132     CSimpleOpt args((argc-1), (++argv), g_rgOptions);
133     while (args.Next())
134     {
135         if (args.LastError() != SO_SUCCESS)
136         {
137             printf("%s: '%s' (use --help to get command line help)\n",
138                    GetLastErrorText(args.LastError()), args.OptionText());
139             ShowUsage(argv[0]);
140             return -1;
141         }
142
143         switch (args.OptionId())
144         {
145
146             case OPT_HELP:
147                 ShowUsage(argv[0]);
148                 return 0;
149
150             case OPT_PORT:
151                 sscanf(args.OptionArg(), "%d", &port); //or atoi()
152                 printf("%s configured to connect to port %d\n", argv[0], port);
153                 break;
154
155             case OPT_SERVER:
156                 szServer = args.OptionArg();
157                 printf("%s configured to connect to the server %s\n", argv[0], szServer);
158                 break;
159
160             case OPT_GAMEID:
161                 gameid = args.OptionArg();
162                 break;
163
164             case OPT_BITNESS:
165                 sscanf(args.OptionArg(), "%d", &bitness);
166                 if (bitness != 32 && bitness != 64)
167                 {
168                     printf("The bitness of a game needs to be either 64-bit or 32-bit.  %d is unknown.\n\n", bitness);
169                     ShowUsage(argv[0]);
170                     return -1;
171                 }
172                 break;
173
174             case OPT_MONITOR:
175                 g_fMonitor = true;
176                 break;
177
178             case OPT_LISTTRACES:
179                 g_fMonitor = true; // Need to wait for the list of trace files to come back
180                 fListTraces = true;
181                 break;
182
183             default:
184                 if (args.OptionArg())
185                 {
186                     printf("option: %2d, text: '%s', arg: '%s'\n",
187                            args.OptionId(), args.OptionText(), args.OptionArg());
188                 }
189                 else
190                 {
191                     printf("option: %2d, text: '%s'\n",
192                            args.OptionId(), args.OptionText());
193                 }
194                 return -1;
195         }
196     }
197
198     if ((NULL == gameid) && (false == g_fMonitor) && (false == fListTraces))
199     {
200         // GameID or monitoring is required
201         printf("Nothing has been asked of the client to do.  See help.\n\n");
202         ShowUsage(argv[0]);
203         return -1;
204     }
205
206
207     //  Setup Ctrl-C handling so we can shut down any active connections properly
208     {
209         sigIntHandler.sa_handler = ctrlc_handler;
210         sigemptyset(&sigIntHandler.sa_mask);
211         sigIntHandler.sa_flags = 0;
212
213         sigaction(SIGINT, &sigIntHandler, NULL);
214     }
215
216     {
217         unsigned int cbBuff = 0;
218         char *pbBuff = NULL;
219         char *vogl_cmd_params = getenv("VOGL_CMD_LINE");
220         const char *gameport = NULL;
221
222         int ret = 0;
223         int cMonitorSleeps = 0;
224
225         std::string strVoglCmdParams;
226
227         if ( NULL != vogl_cmd_params)
228             strVoglCmdParams += vogl_cmd_params;
229
230         if (0 == strcmp(szServer, g_localhost))
231         {
232             //  For localhost, then use a different port for the server and game and then launch the server.
233             gameport = "6125";
234             //strVoglCmdParams += " --vogl_traceport ";
235             //strVoglCmdParams += gameport; // This says what the game should listen on
236
237             system("./voglserver64 -p 6120 2>/tmp/voglserver_stderr.txt 1>/tmp/voglserver_stdout.txt&");
238             port = 6120;
239         }
240
241         network::channelmgr *pChannelMgr = NULL;
242
243         pChannelMgr = new network::channelmgr();
244         if (NULL == pChannelMgr)
245         {
246             printf("%s:%d %s Unable to allocation networking channelmgr.  OOM?\n", __FILE__, __LINE__, __func__);
247             return 1;
248         }
249
250         //  Create a client channel
251         ec = pChannelMgr->Connect( szServer, port, (SENDASYNC|RECVASYNC), ProcessCommand, NULL );
252         if (network::EC_NONE != ec)
253         {
254             printf("%s:%d %s Unable to connect to server %s\nExiting.\n", __FILE__, __LINE__, __func__, szServer);
255             return 1;
256         }
257
258         g_pChannelMgr = pChannelMgr;
259
260         //
261         //  Need to ensure that our recvasync thread is up and running...
262
263
264         //  Launch a game
265         //
266         if (NULL != gameid)
267         {
268             ret = LaunchSteamGameReq(gameid, strVoglCmdParams.c_str(), bitness, gameport, &cbBuff, &pbBuff);
269             if (0 != ret)
270             {
271                 printf("%s:%d %s Unable to serialize Launch Game command (error = %x)\n", __FILE__, __LINE__, __func__, ret);
272                 return -1;
273             }
274
275             ec = pChannelMgr->SendData(cbBuff, pbBuff);
276             if (network::EC_NONE != ec)
277             {
278                 printf("%s:%d %s Client: Error sending message to server (%d): errno = %d\n", __FILE__, __LINE__, __func__, ec, errno);
279             }
280
281             free(pbBuff);
282             pbBuff = NULL;
283         }
284
285
286         //
287         //  List the available trace files
288         //
289         if (true == fListTraces)
290         {
291             ret = AskForTraceFileList(&cbBuff, &pbBuff);
292             if (0 != ret)
293             {
294                 printf("%s:%d %s Unable to serialize Listing Traces command (error = %x)\n", __FILE__, __LINE__, __func__, ec);
295                 return -1;
296             }
297
298             ec = pChannelMgr->SendData(cbBuff, pbBuff);
299             if (network::EC_NONE != ec)
300             {
301                 printf("%s:%d %s Client: Error sending Listing Traces message to server (%d): errno = %d\n", __FILE__, __LINE__, __func__, ec, errno);
302             }
303
304             free(pbBuff);
305             pbBuff = NULL;
306         }
307
308
309         cMonitorSleeps = 0;
310         while (g_fMonitor)
311         {
312
313             //  Every 20 seconds, ping for status from the game
314             sleep(1);
315             cMonitorSleeps++;
316
317             if ((cMonitorSleeps % 20) == 0)
318             {
319                 ret = PingGameReq(cMonitorSleeps, &cbBuff, &pbBuff);
320                 if (0 != ret)
321                 {
322                     printf("%s:%d %s Unable to serialize ping command (error = %x)\n", __FILE__, __LINE__, __func__, ret);
323                     continue;
324                 }
325
326                 ec = pChannelMgr->SendData(cbBuff, pbBuff);
327                 if (network::EC_NONE != ec)
328                 {
329                     printf("%s:%d %s Client: Error sending message to game (%d): errno = %d\n", __FILE__, __LINE__, __func__, ec, errno);
330                 }
331
332                 free(pbBuff);
333                 pbBuff = NULL;
334                 cbBuff = 0;
335             }
336         }
337
338         //(void) pChannelMgr->SendData(strlen("done"), "done");
339         g_pChannelMgr = NULL;
340         pChannelMgr->Disconnect();
341         delete pChannelMgr;
342     }
343
344     return 0;
345 }
346
347
348 void ShowUsage(char *szAppName)
349 {
350     printf("Usage: %s [Options]\n\n", szAppName);
351     printf("Where Options are:\n\n");
352
353     printf("Setting Ports:\n");
354     printf("Specifies the port that this client should connect to.  The default value is %d.\n", DEFAULT_PORT);
355     printf("-p <portNumber>\n");
356     printf("-port <portNumber>\n");
357     printf("--port <portNumber>\n\n");
358
359     printf("Server name:\n");
360     printf("Specifies the name of the server we want to connect to to start debugging.\n");
361     printf("The default value is \"%s\".\n", g_localhost);
362     printf("-s hostname\n");
363     printf("-server hostname\n");
364     printf("--server hostname\n\n");
365
366     printf("Game:\n");
367     printf("Identifies the game to be launched\nIt can identify the game by it's id or name.\n");
368     printf("To see a list of games supported by name, use --listgames\n");
369     printf("-g <game>\n");
370     printf("-gameid <game>\n");
371     printf("--gameid <game>\n\n");
372
373     printf("ListGames:\n");
374     printf("Lists the known set of names for games.  Use the appid of the game if you don't see your game listed here.\n");
375     printf("-lg\n");
376     printf("-listgames\n");
377     printf("--listgames\n\n");
378
379     printf("Bitness:\n");
380     printf("Specifies if the game being launched is 64-bit or 32-bit (defaults to 32)\n");
381     printf("-b [32|64]\n");
382     printf("-bitness [32|64]\n");
383     printf("--bitness [32|64]\n\n");
384
385     printf("Monitor:\n");
386     printf("Keep the client running to monitor the activity on the server\n");
387     printf("-m\n");
388     printf("-monitor\n");
389     printf("--monitor\n\n");
390
391     printf("Capture:\n");
392     printf("Captures a trace file of the game/application being traced\n");
393     printf("-c <framecount> <basefilename>\n");
394     printf("-capture <framecount> <basefilename>\n");
395     printf("--capture <framecount> <basefilename>\n");
396     printf("Where, \n");
397     printf("[required] <framecount> - is the number of frames to capture in the trace.\n");
398     printf("[optional] <basefilename> - is the base file name for the trace file.\n\n");
399
400     printf("Retrieve:\n");
401     printf("Retrieves a remote capture file and brings it locally.\n");
402     printf("If the file is already local, this doesn't do anything.\n");
403     printf("-r <filename>\n");
404     printf("-retrieve <filename>\n");
405     printf("--retrieve <filename>\n");
406     printf("Where, \n");
407     printf("[required] <filename> - is the name of the file to bring locally.  This is just the filename\n");
408     printf("  and does not include a path.  The filename will be looked for on the remote host in the default\n");
409     printf("  location that traces are stored.\n\n");
410
411     printf("ListTraces:\n");
412     printf("Lists the set of traces available for download.\n");
413     printf("-lt\n");
414     printf("-listtraces\n");
415     printf("--listtraces\n\n");
416
417     printf("Help message:\n");
418     printf("Gives this useful help message again.\n");
419     printf("-?\n");
420     printf("-h\n");
421     printf("-help\n");
422     printf("--help\n\n");
423 }
424
425 static const char *
426 GetLastErrorText(int a_nError)
427 {
428     switch (a_nError)
429     {
430         case SO_SUCCESS:
431             return ("Success");
432         case SO_OPT_INVALID:
433             return ("Unrecognized option");
434         case SO_OPT_MULTIPLE:
435             return ("Option matched multiple strings");
436         case SO_ARG_INVALID:
437             return ("Option does not accept argument");
438         case SO_ARG_INVALID_TYPE:
439             return ("Invalid argument format");
440         case SO_ARG_MISSING:
441             return ("Required argument is missing");
442         case SO_ARG_INVALID_DATA:
443             return ("Invalid argument data");
444         default:
445             return ("Unknown error");
446     }
447 }
448
449 void ctrlc_handler(int s)
450 {
451     printf("Caught signal %d\n",s);
452
453     if (NULL != g_pChannelMgr)
454     {
455         g_pChannelMgr->Disconnect();
456         delete g_pChannelMgr;
457
458         g_pChannelMgr = NULL;
459     }
460     exit(1);
461 #ifdef NEVER
462     g_fMonitor = false;  //  Tell the main loop to exit and shutdown
463
464     return;
465 #endif // NEVER
466 }
467
468 void ProcessCommand(void * /*callbackParam*/, unsigned int buffer_size, char *buffer)
469 {
470     int32_t command = -1;
471     int ec = 0;
472
473     command = *(int32_t *)buffer;
474
475     unsigned int buffer_size_temp = buffer_size - sizeof(int32_t);
476     char *buffer_temp = buffer + sizeof(int32_t);
477
478     switch (command)
479     {
480         case TOCLIENTMSG:
481         {
482             ec = PrintToClientMsg(buffer_size_temp, buffer_temp);
483             if (0 != ec)
484             {
485                 //  Failed to parse?
486                 printf("Unable to parse TOCLIENTMSG json\n");
487                 break;
488             }
489             break;
490         }
491
492         case TRACE_LIST:
493         {
494             ec = DumpTraceFileList(buffer_size_temp, buffer_temp);
495             if (0 != ec)
496             {
497                 //  Failed to parse?
498                 printf("Unable to parse TRACE_LIST json\n");
499                 break;
500             }
501             break;
502         }
503
504         default:
505         {
506             printf("Unknown command sent to client: %d  Dropped.\n", command);
507             break;
508         }
509     }
510
511     return;
512 }
513