]> git.cworth.org Git - vogl/blob - src/vogltrace/vogl_remote.cpp
Initial vogl checkin
[vogl] / src / vogltrace / vogl_remote.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 #if VOGL_REMOTING
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <signal.h>
35 #include <time.h>
36
37 #include <pthread.h>
38
39 #include <vogl_core.h>
40 #include <vogl_json.h> 
41
42 #include "vogl_intercept.h"
43
44 #include "../common/vogllogging.h"
45 #include "../common/channel.h"
46
47 #include "../common/commands.h"
48 #include "../common/toclientmsg.h"
49 #include "../common/pinggame.h"
50
51
52 // Needs to be the same in the server gpusession code
53 int g_vogl_traceport = TRACE_PORT;
54 char g_procname[256] = { 0 };
55
56 void *invoke_listener(void *arg);
57
58 bool log_output_func(vogl::eConsoleMessageType type, const char *pMsg, void * /*pData*/);
59 void send_status_to_client(const char *message);
60 void send_status_to_client(const char *message, int token);
61
62 void vogl_init_listener(int vogl_traceport)
63 {
64     pthread_t threadCmd;
65     int err = 0;
66
67     openlog("vogl-remote", LOG_PID | LOG_CONS | LOG_PERROR, LOG_USER);
68
69     //
70     //  Get the process name  
71     //    If, in the unlikely event, the name of the process doesn't fit, then readlink won't 0-terminate the string.
72     //    We initialize it to all 0, so if it all got overwritten, then 0 out the last character in the string.
73     //
74     (void) readlink("/proc/self/exe", g_procname, sizeof(g_procname));
75     g_procname[sizeof(g_procname)-1] = '\0';
76
77     
78
79     //  Launching a VOGL Listener
80     if (0 == vogl_traceport)
81     {
82         vogl_traceport = TRACE_PORT;
83         syslog(VOGL_INFO, "No port # passed in, %s listening on default port %d\n", g_procname, vogl_traceport);
84     }
85
86     syslog(VOGL_INFO, "Spinning up listener for vogl_remoting of %s on port %d...\n", g_procname, vogl_traceport);
87
88     g_vogl_traceport = vogl_traceport;
89
90     err = pthread_create(&threadCmd, NULL, invoke_listener, NULL);
91     if (0 != err)
92     {
93         syslog(VOGL_ERROR, "%s:%d %s  Unable to create vogl_remoting cmd thread. returned %d, errno %d\n", __FILE__, __LINE__, __func__, err, errno);
94         return;
95     }
96
97     err = pthread_setname_np(threadCmd, "vogl_remoting");
98     if (0 != err)
99     {
100         syslog(VOGL_ERROR, "%s:%d %s  Unable to set the name of the vogl_remoting cmd thread. returned %d, errno %d\n", __FILE__, __LINE__, __func__, err, errno);
101         return;
102     }
103
104     return;
105 }
106
107 void process_trace_command(void *callback_param, unsigned int buffer_size, char *buffer);
108
109 bool g_fKillApp = false;
110 network::channelmgr *g_clientChannelMgr = NULL;
111
112 void *
113 invoke_listener(void * /*arg*/) 
114 {
115     //
116     //  Setup to wait for a client to connect
117     //
118     pthread_t thisThread = pthread_self();
119     network::CHEC chec = network::EC_NONE;
120
121     int err = 0;
122
123     err = pthread_setname_np(thisThread, "vogl_listen");
124     if (0 != err)
125     {
126         syslog(VOGL_ERROR, "%s:%d %s Unable to set the name of the thread. returned %d, errno %d\n", __FILE__, __LINE__, __func__, err, errno);
127     }
128
129     syslog(VOGL_INFO, "Listener for VOGL Tracing in operation...\n");
130
131     g_clientChannelMgr = new network::channelmgr();
132     if (NULL == g_clientChannelMgr)
133     {
134         syslog(VOGL_ERROR, "%s:%d %s Unable to allocation networking channelmgr.  OOM?\n", __FILE__, __LINE__, __func__);
135         return NULL;
136     }
137
138     //  Create a client channel
139     chec = g_clientChannelMgr->Connect( const_cast<char *>("localhost"), g_vogl_traceport, (SENDASYNC|RECVASYNC), process_trace_command, NULL );
140     //chec = g_clientChannelMgr->Accept( g_vogl_traceport, (RECVASYNC|SENDASYNC), true,  process_trace_command, NULL, NULL, NULL );
141     if (network::EC_NONE != chec)
142     {
143         syslog(VOGL_ERROR, "%s:%d %s Unable to connect on port %d\nExiting.\n", __FILE__, __LINE__, __func__, g_vogl_traceport);
144         return NULL;
145     }
146
147     //
148     //  Tell the client we're up and running.
149     send_status_to_client("Game is up and running.");
150
151     //
152     //  Start capturing output from the tracer
153     vogl::console::add_console_output_func(log_output_func, NULL);
154
155     while (true)
156     {
157         if (g_fKillApp)
158         {
159             break;
160         }
161         sleep(1);  //  Don't busy spin...
162     }
163
164     if (g_fKillApp)
165     {
166         syslog(VOGL_INFO, "%lx: VOGL Tracing server thread: Terminating the application.\n", thisThread);
167         kill(getpid(), SIGTERM); // SIGINT);
168     }
169
170     syslog(VOGL_INFO, "%lx: VOGL Tracing server thread: Terminating.\n", thisThread);
171
172     return NULL;
173 }
174
175 bool SetNullMode(unsigned int buffer_size, char *buffer);
176 bool SetDumpGLCalls(unsigned int buffer_size, char *buffer);
177 bool SetDumpGLBuffers(unsigned int buffer_size, char *buffer);
178 bool SetDumpGLShaders(unsigned int buffer_size, char *buffer);
179 bool SetBackTrace(unsigned int buffer_size, char *buffer);
180
181 bool KillTracer(unsigned int buffer_size, char *buffer);
182 int StartCapture(unsigned int buffer_size, char *buffer);
183 int StopCapture(unsigned int buffer_size, char *buffer);
184
185 extern bool g_null_mode;
186 extern bool g_backtrace_all_calls;
187 extern bool g_dump_gl_calls_flag;
188 extern bool g_dump_gl_buffers_flag;
189 extern bool g_dump_gl_shaders_flag;
190
191 //  Async calls to send contents to our client
192 //
193 void CaptureCompleteCallback(const char *pFilename, void *pOpaque);
194
195
196 void process_trace_command(void * /*callback_param*/, unsigned int buffer_size, char *buffer)
197 {
198     int err = 0;
199     int32_t command = -1;
200
201     command = *(int32_t *)buffer;
202
203     unsigned int buffer_size_temp = buffer_size - sizeof(int32_t);
204     char *buffer_temp = buffer + sizeof(int32_t);
205
206     switch (command)
207     {
208         case TRACE_SETNULLMODE:
209         {
210             g_null_mode = SetNullMode(buffer_size_temp, buffer_temp);
211
212             break;
213         }
214
215         case TRACE_SETDUMPGLCALLS:
216         {
217             g_dump_gl_calls_flag = SetDumpGLCalls(buffer_size_temp, buffer_temp);
218
219             break;
220         }
221
222         case TRACE_SETDUMPGLBUFFERS:
223         {
224             g_dump_gl_buffers_flag = SetDumpGLBuffers(buffer_size_temp, buffer_temp);
225
226             break;
227         }
228
229         case TRACE_SETDUMPGLSHADERS:
230         {
231             g_dump_gl_shaders_flag = SetDumpGLShaders(buffer_size_temp, buffer_temp);
232
233             break;
234         }
235
236         case TRACE_SETBACKTRACE:
237         {
238             g_backtrace_all_calls = SetBackTrace(buffer_size_temp, buffer_temp);
239
240             break;
241         }
242
243         case TRACE_KILLTRACER:
244         {
245             g_fKillApp = KillTracer(buffer_size_temp, buffer_temp);
246
247             break;
248         }
249
250         case TRACE_STARTCAPTURE:
251         {
252
253             err = StartCapture(buffer_size_temp, buffer_temp);
254
255             if (0 != err)
256             {
257                 syslog(VOGL_ERROR, "%s:%d %s  StartCapture() failed\n", __FILE__, __LINE__, __func__);
258             }
259
260             break;
261         }
262
263         case TRACE_STOPCAPTURE:
264         {
265
266             err = StopCapture(buffer_size_temp, buffer_temp);
267
268             if (0 != err)
269             {
270                 syslog(VOGL_ERROR, "%s:%d %s  StopCapture() failed\n", __FILE__, __LINE__, __func__);
271             }
272
273             break;
274         }
275
276         case PING_GAME:
277         {
278             int notif_id = -1;
279             notif_id = PingGame(buffer_size_temp, buffer_temp);
280             //
281             //  Tell the client we're up and running.
282             send_status_to_client("Game is up and running.", notif_id);
283
284             break;
285         }
286
287         default:
288         {
289             syslog(VOGL_ERROR, "%s:%d %s  Unknown method (%d)\n", __FILE__, __LINE__, __func__, command);
290             break;
291         }
292     }
293
294     return;
295 }
296
297 //
298 //  SetNullModePB
299 //
300 bool SetNullMode(unsigned int buffer_size, char *buffer)
301 {
302     vogl::json_document cur_doc;
303
304     if (0 == buffer_size)
305         return false;
306
307     cur_doc.deserialize(buffer);
308     vogl::json_node *pjson_node = cur_doc.get_root()->find_child_object("parameters");
309
310     return pjson_node->value_as_bool("nullmode", false);
311 }
312
313 //
314 //  SetDumpGLCallsPB
315 //
316 bool SetDumpGLCalls(unsigned int buffer_size, char *buffer)
317 {
318     vogl::json_document cur_doc;
319
320     if (0 == buffer_size)
321         return false;
322
323     cur_doc.deserialize(buffer);
324     vogl::json_node *pjson_node = cur_doc.get_root()->find_child_object("parameters");
325
326     return pjson_node->value_as_bool("dumpglcalls", false);
327 }
328
329 //
330 //  SetDumpGLBuffersPB
331 //
332 //
333 bool SetDumpGLBuffers(unsigned int buffer_size, char *buffer)
334 {
335     vogl::json_document cur_doc;
336
337     if (0 == buffer_size)
338         return false;
339
340     cur_doc.deserialize(buffer);
341     vogl::json_node *pjson_node = cur_doc.get_root()->find_child_object("parameters");
342
343     return pjson_node->value_as_bool("dumpglbuffers", false);
344 }
345
346 //
347 //  SetDumpGLShaders
348 //
349 //
350 bool SetDumpGLShaders(unsigned int buffer_size, char *buffer)
351 {
352     vogl::json_document cur_doc;
353
354     if (0 == buffer_size)
355         return false;
356
357     cur_doc.deserialize(buffer);
358     vogl::json_node *pjson_node = cur_doc.get_root()->find_child_object("parameters");
359
360     return pjson_node->value_as_bool("dumpglshaders", false);
361 }
362
363 //
364 //  SetBackTrace
365 //
366 //  Parameters:
367 //    If turned on, captures callstacks for any GL call and put it in the tracefile.
368 //
369 bool SetBackTrace(unsigned int buffer_size, char *buffer)
370 {
371     vogl::json_document cur_doc;
372
373     if (0 == buffer_size)
374         return false;
375
376     cur_doc.deserialize(buffer);
377     vogl::json_node *pjson_node = cur_doc.get_root()->find_child_object("parameters");
378
379     return pjson_node->value_as_bool("backtrace", false);
380 }
381
382 //
383 //  KillTracer
384 //
385 //  Kills the application that is currently being traced.
386 //
387 bool KillTracer(unsigned int /*buffer_size*/, char * /*buffer*/)
388 {
389     // No body to this message.  Just an instruction.
390     return true;
391 }
392
393
394 char szTraceFileLocal[] = "/.local/share";
395
396 int StartCapture(unsigned int buffer_size, char *buffer)
397 {
398     const char *szBaseFileName;
399     int cFrames = 0;
400     bool fWorked = true;
401     vogl::json_document cur_doc;
402     vogl::json_node *pjson_node;
403     int status = 0;
404
405     std::string strTraceLocation;
406     char *szTraceLocationT = NULL;
407
408     syslog(VOGL_INFO, "Checking to see if we're already capturing\n");
409
410     if (vogl_is_capturing())
411     {
412         goto out;
413     }
414
415     if (0 == buffer_size)
416     {
417         syslog(VOGL_ERROR, "%s:%d %s  No parameters passed\n", __FILE__, __LINE__, __func__);
418         goto out;
419     }
420
421     cur_doc.deserialize(buffer);
422     pjson_node = cur_doc.get_root()->find_child_object("parameters");
423     
424     syslog(VOGL_INFO, "Not already capturing...continuing\n");
425
426     cFrames = pjson_node->value_as_int("framestocapture", -1);
427     szBaseFileName = pjson_node->value_as_string_ptr("tracename", "");
428
429     syslog(VOGL_INFO, "Capturing to %s for %d frames\n", szBaseFileName, cFrames);
430
431
432     //
433     //  Handle the destination directory for the file as per spec
434     //    $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored. 
435     //    If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used.
436     szTraceLocationT = getenv("XDG_DATA_HOME");
437     syslog(VOGL_INFO, "XDG_DATA_HOME is '%s'\n", (NULL == szTraceLocationT? "NULL": szTraceLocationT));
438     if (NULL == szTraceLocationT)
439     {
440         syslog(VOGL_INFO, "Building strTraceLocation from '%s' + '%s'\n", getenv("HOME"), szTraceFileLocal);
441         strTraceLocation = getenv("HOME");
442         strTraceLocation += szTraceFileLocal;
443     }
444     else
445     {
446         strTraceLocation = szTraceLocationT;
447     }
448     strTraceLocation += "/vogl/";
449
450     syslog(VOGL_INFO, "Will be tracing to '%s'\n", strTraceLocation.c_str());
451
452     //  Ensure the destination directory is actually there
453     status = mkdir(strTraceLocation.c_str(), 0777 );
454     if (-1 == status && EEXIST != errno)
455     {
456         syslog(VOGL_INFO, "StartCapturePB: Unable to create trace destination directory (%d) %s\n", errno, strTraceLocation.c_str());
457
458         return -1;
459     }
460
461     syslog(VOGL_INFO, "StartCapturePB: Capturing trace to %s%s for %d frames\n", strTraceLocation.c_str(), szBaseFileName, cFrames);
462
463     fWorked = vogl_capture_on_next_swap(cFrames, strTraceLocation.c_str(), szBaseFileName, CaptureCompleteCallback, NULL);
464
465     syslog(VOGL_INFO, "StartCapturePB: Tracing now started (%s).  Trace Basefilename = %s\n", (fWorked ? "success" : "failed"), szBaseFileName);
466
467 out:
468
469     return 0;
470 }
471
472
473 int StopCapture(unsigned int /*buffer_size*/, char * /*buffer*/)
474 {
475     bool fStillCapturing = false;
476     bool fStopping = false;
477
478     //
479     //  No body to this message.  Not necessary.
480
481     fStillCapturing = vogl_is_capturing();
482     syslog(VOGL_INFO, "StopCapturePB(): Trace status = %d\n", fStillCapturing);
483
484     if (true == fStillCapturing)
485     {
486         //  Need to terminate the trace
487         fStopping = vogl_stop_capturing(CaptureCompleteCallback, NULL);
488         syslog(VOGL_INFO, "StopCapturePB(): vogl_stop_capturing status = %d\n", fStopping);
489     }
490
491     return 0;
492 }
493
494
495 //
496 //  Sends off a message to the client that tracing has completed.
497 //
498 int CaptureCompleteCallbackMsg(const char *pFilename, int cFramesCaptured, unsigned int *message_size, char **pmessage);
499 void CaptureCompleteCallback(const char *pFilename, void * /*pOpaque*/)
500 {
501     int ec = 0;
502     network::CHEC chec = network::EC_NONE;
503     char *message = NULL;
504     unsigned int message_size = 0;
505     int cFramesCaptured = -1;
506
507     syslog(VOGL_INFO, "CaptureCompleteCallback(): Tracing complete...\n");
508
509     //  Send off a status message to the client with:
510     //  process_name
511     //  file_name
512     //  frames_capture (TBD)
513     //  Anything else?
514     ec = CaptureCompleteCallbackMsg(pFilename, cFramesCaptured, &message_size, &message);
515     if (0 != ec)
516     {
517         syslog(VOGL_ERROR, "%s:%d %s  Unable to send tracefile name (%s) back to client - OOM?\n", __FILE__, __LINE__, __func__, pFilename);
518         return;       
519     }
520
521     chec = g_clientChannelMgr->SendData(message_size, message);
522     if (network::EC_NONE != chec)
523     {
524         syslog(VOGL_ERROR, "%s:%d %s  Unable to send tracefile name (%s) back to client - Network error.\n", __FILE__, __LINE__, __func__, pFilename);
525     }
526
527     if (message)
528         free(message);
529
530     return;
531 }
532
533
534 int CaptureCompleteCallbackMsg(const char *pFilename, int cFramesCaptured, unsigned int *message_size, char **pmessage)
535 {
536     vogl::json_document cur_doc;
537     vogl::dynamic_string dst;
538
539     char *pbBuff;
540     unsigned int cbBuff;
541
542     vogl::json_node &meta_node = cur_doc.get_root()->add_object("parameters");
543
544     meta_node.add_key_value("trace_file_name", pFilename);
545     meta_node.add_key_value("process_name", g_procname);
546     meta_node.add_key_value("frames_captured", cFramesCaptured);
547
548     cur_doc.serialize(dst);
549
550     cbBuff = dst.get_len() + 1 + sizeof(int);
551     pbBuff = (char *)malloc(cbBuff);
552     if (NULL == pbBuff)
553     {
554         printf("OOM\n");
555         return -1;
556     }
557
558     //  First part of buffer is the command id
559     *((int *)pbBuff) = TRACE_CAPTURESTATUS;
560     strncpy((char *)(pbBuff+sizeof(int)), dst.get_ptr(), cbBuff - sizeof(int));
561
562     *pmessage = pbBuff;
563     *message_size = cbBuff;
564
565     return 0;
566 }
567
568
569 bool log_output_func(vogl::eConsoleMessageType /*type*/, const char *pMsg, void * /*pData*/)
570 {
571
572     send_status_to_client(pMsg);
573
574     return true;
575 }
576
577
578 void send_status_to_client(const char *message)
579 {
580     int ec = 0;
581     unsigned int cbBuff = 0;
582     char *pbBuff = NULL;
583     //
584     //  Prepare message to send
585
586     ec = ToClientMsgReq(g_procname, 0, message, &cbBuff, &pbBuff);
587
588     (void)g_clientChannelMgr->SendData(cbBuff, pbBuff);
589
590     if (pbBuff)
591         free(pbBuff);
592
593     return;
594 }
595
596 void send_status_to_client(const char *message, int /*token*/)
597 {
598     //  Ignoring token for a moment.
599     send_status_to_client(message);
600
601     return;
602 }
603
604 #endif // VOGL_REMOTING