1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
24 **************************************************************************/
35 #include <vogl_core.h>
36 #include <vogl_json.h>
38 #include "../common/SimpleOpt.h"
39 #include "../common/channel.h"
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"
63 CSimpleOpt::SOption g_rgOptions[] =
65 { OPT_HELP, "-?", SO_NONE },
66 { OPT_HELP, "-h", SO_NONE },
67 { OPT_HELP, "-help", SO_NONE },
68 { OPT_HELP, "--help", SO_NONE },
70 { OPT_PORT, "-p", SO_REQ_SEP },
71 { OPT_PORT, "-port", SO_REQ_SEP },
72 { OPT_PORT, "--port", SO_REQ_SEP },
74 { OPT_SERVER, "-s", SO_REQ_SEP },
75 { OPT_SERVER, "-server", SO_REQ_SEP },
76 { OPT_SERVER, "--server", SO_REQ_SEP },
78 { OPT_GAMEID, "--game", SO_REQ_SEP },
79 { OPT_GAMEID, "-game", SO_REQ_SEP },
80 { OPT_GAMEID, "-g", SO_REQ_SEP },
82 { OPT_LISTGAMES, "--listgames", SO_NONE },
83 { OPT_LISTGAMES, "-listgames", SO_NONE },
84 { OPT_LISTGAMES, "-lg", SO_NONE },
86 { OPT_BITNESS, "--bitness", SO_REQ_SEP },
87 { OPT_BITNESS, "-bitness", SO_REQ_SEP },
88 { OPT_BITNESS, "-b", SO_REQ_SEP },
90 { OPT_MONITOR, "--monitor", SO_NONE },
91 { OPT_MONITOR, "-monitor", SO_NONE },
92 { OPT_MONITOR, "-m", SO_NONE },
94 { OPT_CAPTURE, "--capture", SO_REQ_SEP },
95 { OPT_CAPTURE, "-capture", SO_REQ_SEP },
96 { OPT_CAPTURE, "-c", SO_REQ_SEP },
98 { OPT_RETRIEVE, "--retrieve", SO_REQ_SEP },
99 { OPT_RETRIEVE, "-retrieve", SO_REQ_SEP },
100 { OPT_RETRIEVE, "-r", SO_REQ_SEP },
102 { OPT_LISTTRACES, "--listtraces", SO_NONE },
103 { OPT_LISTTRACES, "-listtraces", SO_NONE },
104 { OPT_LISTTRACES, "-lt", SO_NONE },
109 void ShowUsage(char *szAppName);
110 static const char *GetLastErrorText(int a_nError);
112 void ctrlc_handler(int s) __attribute__ ((noreturn));
114 char g_localhost[] = "localhost";
115 bool g_fMonitor = false;
116 network::channelmgr *g_pChannelMgr = NULL;
118 void ProcessCommand(void *callbackParam, unsigned int cbData, char *pbData);
121 main_trace(int argc, char *argv[])
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;
132 CSimpleOpt args((argc-1), (++argv), g_rgOptions);
135 if (args.LastError() != SO_SUCCESS)
137 printf("%s: '%s' (use --help to get command line help)\n",
138 GetLastErrorText(args.LastError()), args.OptionText());
143 switch (args.OptionId())
151 sscanf(args.OptionArg(), "%d", &port); //or atoi()
152 printf("%s configured to connect to port %d\n", argv[0], port);
156 szServer = args.OptionArg();
157 printf("%s configured to connect to the server %s\n", argv[0], szServer);
161 gameid = args.OptionArg();
165 sscanf(args.OptionArg(), "%d", &bitness);
166 if (bitness != 32 && bitness != 64)
168 printf("The bitness of a game needs to be either 64-bit or 32-bit. %d is unknown.\n\n", bitness);
179 g_fMonitor = true; // Need to wait for the list of trace files to come back
184 if (args.OptionArg())
186 printf("option: %2d, text: '%s', arg: '%s'\n",
187 args.OptionId(), args.OptionText(), args.OptionArg());
191 printf("option: %2d, text: '%s'\n",
192 args.OptionId(), args.OptionText());
198 if ((NULL == gameid) && (false == g_fMonitor) && (false == fListTraces))
200 // GameID or monitoring is required
201 printf("Nothing has been asked of the client to do. See help.\n\n");
207 // Setup Ctrl-C handling so we can shut down any active connections properly
209 sigIntHandler.sa_handler = ctrlc_handler;
210 sigemptyset(&sigIntHandler.sa_mask);
211 sigIntHandler.sa_flags = 0;
213 sigaction(SIGINT, &sigIntHandler, NULL);
217 unsigned int cbBuff = 0;
219 char *vogl_cmd_params = getenv("VOGL_CMD_LINE");
220 const char *gameport = NULL;
223 int cMonitorSleeps = 0;
225 std::string strVoglCmdParams;
227 if ( NULL != vogl_cmd_params)
228 strVoglCmdParams += vogl_cmd_params;
230 if (0 == strcmp(szServer, g_localhost))
232 // For localhost, then use a different port for the server and game and then launch the server.
234 //strVoglCmdParams += " --vogl_traceport ";
235 //strVoglCmdParams += gameport; // This says what the game should listen on
237 system("./voglserver64 -p 6120 2>/tmp/voglserver_stderr.txt 1>/tmp/voglserver_stdout.txt&");
241 network::channelmgr *pChannelMgr = NULL;
243 pChannelMgr = new network::channelmgr();
244 if (NULL == pChannelMgr)
246 printf("%s:%d %s Unable to allocation networking channelmgr. OOM?\n", __FILE__, __LINE__, __func__);
250 // Create a client channel
251 ec = pChannelMgr->Connect( szServer, port, (SENDASYNC|RECVASYNC), ProcessCommand, NULL );
252 if (network::EC_NONE != ec)
254 printf("%s:%d %s Unable to connect to server %s\nExiting.\n", __FILE__, __LINE__, __func__, szServer);
258 g_pChannelMgr = pChannelMgr;
261 // Need to ensure that our recvasync thread is up and running...
268 ret = LaunchSteamGameReq(gameid, strVoglCmdParams.c_str(), bitness, gameport, &cbBuff, &pbBuff);
271 printf("%s:%d %s Unable to serialize Launch Game command (error = %x)\n", __FILE__, __LINE__, __func__, ret);
275 ec = pChannelMgr->SendData(cbBuff, pbBuff);
276 if (network::EC_NONE != ec)
278 printf("%s:%d %s Client: Error sending message to server (%d): errno = %d\n", __FILE__, __LINE__, __func__, ec, errno);
287 // List the available trace files
289 if (true == fListTraces)
291 ret = AskForTraceFileList(&cbBuff, &pbBuff);
294 printf("%s:%d %s Unable to serialize Listing Traces command (error = %x)\n", __FILE__, __LINE__, __func__, ec);
298 ec = pChannelMgr->SendData(cbBuff, pbBuff);
299 if (network::EC_NONE != ec)
301 printf("%s:%d %s Client: Error sending Listing Traces message to server (%d): errno = %d\n", __FILE__, __LINE__, __func__, ec, errno);
313 // Every 20 seconds, ping for status from the game
317 if ((cMonitorSleeps % 20) == 0)
319 ret = PingGameReq(cMonitorSleeps, &cbBuff, &pbBuff);
322 printf("%s:%d %s Unable to serialize ping command (error = %x)\n", __FILE__, __LINE__, __func__, ret);
326 ec = pChannelMgr->SendData(cbBuff, pbBuff);
327 if (network::EC_NONE != ec)
329 printf("%s:%d %s Client: Error sending message to game (%d): errno = %d\n", __FILE__, __LINE__, __func__, ec, errno);
338 //(void) pChannelMgr->SendData(strlen("done"), "done");
339 g_pChannelMgr = NULL;
340 pChannelMgr->Disconnect();
348 void ShowUsage(char *szAppName)
350 printf("Usage: %s [Options]\n\n", szAppName);
351 printf("Where Options are:\n\n");
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");
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");
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");
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");
376 printf("-listgames\n");
377 printf("--listgames\n\n");
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");
385 printf("Monitor:\n");
386 printf("Keep the client running to monitor the activity on the server\n");
388 printf("-monitor\n");
389 printf("--monitor\n\n");
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");
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");
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");
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");
411 printf("ListTraces:\n");
412 printf("Lists the set of traces available for download.\n");
414 printf("-listtraces\n");
415 printf("--listtraces\n\n");
417 printf("Help message:\n");
418 printf("Gives this useful help message again.\n");
422 printf("--help\n\n");
426 GetLastErrorText(int a_nError)
433 return ("Unrecognized option");
434 case SO_OPT_MULTIPLE:
435 return ("Option matched multiple strings");
437 return ("Option does not accept argument");
438 case SO_ARG_INVALID_TYPE:
439 return ("Invalid argument format");
441 return ("Required argument is missing");
442 case SO_ARG_INVALID_DATA:
443 return ("Invalid argument data");
445 return ("Unknown error");
449 void ctrlc_handler(int s)
451 printf("Caught signal %d\n",s);
453 if (NULL != g_pChannelMgr)
455 g_pChannelMgr->Disconnect();
456 delete g_pChannelMgr;
458 g_pChannelMgr = NULL;
462 g_fMonitor = false; // Tell the main loop to exit and shutdown
468 void ProcessCommand(void * /*callbackParam*/, unsigned int buffer_size, char *buffer)
470 int32_t command = -1;
473 command = *(int32_t *)buffer;
475 unsigned int buffer_size_temp = buffer_size - sizeof(int32_t);
476 char *buffer_temp = buffer + sizeof(int32_t);
482 ec = PrintToClientMsg(buffer_size_temp, buffer_temp);
486 printf("Unable to parse TOCLIENTMSG json\n");
494 ec = DumpTraceFileList(buffer_size_temp, buffer_temp);
498 printf("Unable to parse TRACE_LIST json\n");
506 printf("Unknown command sent to client: %d Dropped.\n", command);