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 **************************************************************************/
32 #include <sys/types.h>
37 #include "../common/radlogging.h"
38 #include "../common/channel.h"
40 #include "../public/rpcec.h"
41 #include "../public/rpcconst.h"
43 #include "vogl_console.h"
45 #include "vogl_remote.h"
47 #include "debugserver.pb.h"
48 #include "gpureplaycontrol.pb.h"
50 // Needs to be the same in the server gpusession code
51 #define REQRESP_PORT 5600
52 #define LOGSTREAM_PORT (REQRESP_PORT + 1)
54 void *InvokeGLIReplayListener(void *arg);
56 static bool g_ping_value; // Just for testing aliveness...
58 void vogl_init_listener()
62 syslog(RAD_INFO, "Spinning up listener for GLI Replayer...\n");
64 // Launching a GLI Listener
65 err = pthread_create(&threadCmd, NULL, InvokeGLIReplayListener, NULL);
68 syslog(RAD_ERROR, "Unable to create GLI Replayer cmd thread. returned %d, errno %d\n", err, errno);
75 bool ProcessCommandGLIReplay(std::string strReq, std::string *pstrResp);
76 RPCEC rpcecFromChec(network::CHEC chec);
78 #define MAX_RETRIES 10
79 #define NET_TIMEOUT 500 // MS
82 InvokeGLIReplayListener(void * /*arg*/) // arg is unused
85 // Setup to wait for a client to connect
87 pthread_t thisThread = pthread_self();
88 RPCEC rpcec = rpcecNone;
89 bool fKeepRunning = true;
90 network::CHEC chec = network::EC_NONE;
92 int port = REQRESP_PORT;
94 g_ping_value = false; // Just for testing aliveness...
98 network::channel *pChannel = NULL;
100 pChannel = new network::channel();
101 if (NULL == pChannel)
103 syslog(RAD_ERROR, "%lx: InvokeGLIReplayListener: Error allocating channel object: OOM\n", thisThread);
107 chec = pChannel->Connect(port, 3, true);
108 if (network::EC_NONE != chec)
110 syslog(RAD_ERROR, "%lx: InvokeGLIReplayListener: Error on ChannelConnect(port = %d, errno = %x): %x\nTerminating...\n", thisThread, port, errno, chec);
117 chec = pChannel->ReadMsg(&strReq, 1, 0);
118 rpcec = rpcecFromChec(chec);
119 if (rpcecNone != rpcec)
124 fKeepRunning = ProcessCommandGLIReplay(strReq, &strResp);
126 chec = pChannel->WriteMsg(strResp, 1, 0);
127 rpcec = rpcecFromChec(chec);
128 if (rpcecNone != rpcec)
133 pChannel->Disconnect();
138 syslog(RAD_INFO, "%lx: GLI Replayer server thread: Terminating.\n", thisThread);
143 RPCEC PingPB(int sessionid, std::string strReq, std::string *pstrResp, int *perrno, std::string *pstrError);
144 //RPCEC RegisterLogCallbackPB(int sessionid, std::string strReq, std::string *pstrResp, int *perrno, std::string *pstrError);
145 //RPCEC DeregisterLogCallbackPB(int sessionid, std::string strReq, std::string *pstrResp, int *perrno, std::string *pstrError);
148 ProcessCommandGLIReplay(std::string strReq, std::string *pstrResp)
150 bool fKeepRunning = true;
151 RPCEC rpcec = rpcecNone;
152 std::string strReqInner;
153 std::string strRespInner;
155 std::string strError;
157 debugserverpb::BaseMethodReqPB gpuBaseReq;
158 debugserverpb::BaseMethodRespPB gpuBaseResp;
163 gpuBaseReq.ParseFromString(strReq);
165 interfaceid = gpuBaseReq.interfaceid();
166 method = gpuBaseReq.method();
167 strReqInner = gpuBaseReq.embeddedrequest();
169 // Should never happen...
170 if (GLIREPLAYCONTROL != interfaceid)
172 // Currently only a single interface is known for GLI. If it's not this one, not sure what's supposed to happen.
173 syslog(RAD_ERROR, "ProcessCommandGLIReplay: GPUReplayerControl: Unknown interfaceid (%d) passed to vogltrace from client\n", interfaceid);
174 strError = "ProcessCommandGLIReplay: GPUReplayerControl: Unknown interfaceid passed to voglreplayer from client";
182 case gpureplaycontrolpb::PING:
184 rpcec = PingPB(gpuBaseReq.dbgsessionid(), strReqInner, &strRespInner, &err, &strError);
186 if (rpcecNone != rpcec)
188 syslog(RAD_ERROR, "ProcessCommandGLIReplay: GPUReplayerControl: Ping() failed\n");
194 case gpucontrolpb::REGISTERLOGCALLBACK:
196 rpcec = RegisterLogCallbackPB(gpuBaseReq.dbgsessionid(), strReqInner, &strRespInner, &err, &strError);
198 if (rpcecNone != rpcec)
200 syslog(RAD_ERROR, "ProcessCommandGLIReplay: GPUReplayerControl: RegisterLogCallback() failed\n");
206 case gpucontrolpb::DEREGISTERLOGCALLBACK:
208 rpcec = DeregisterLogCallbackPB(gpuBaseReq.dbgsessionid(), strReqInner, &strRespInner, &err, &strError);
210 if (rpcecNone != rpcec)
212 syslog(RAD_ERROR, "ProcessCommandGLIReplay: GPUReplayerControl: DeregisterLogCallback() failed\n");
222 syslog(RAD_ERROR, "ProcessCommandGLIReplay: Unknown method (%d) for GPUReplayerControl interface\n", method);
223 strError = "ProcessCommandGLIReplay: GPUReplayerControl: Unknown method passed to vogltrace interface from client";
230 // Set the method back into the response
232 gpuBaseResp.set_interfaceid(interfaceid);
233 gpuBaseResp.set_method(method);
234 gpuBaseResp.set_rpcec(rpcec);
235 if (rpcecNone != rpcec)
237 syslog(RAD_ERROR, "ProcessCommandGLIReplay: GPUReplayerControl: returning errors to client (rpcec = %d)\n", rpcec);
238 gpuBaseResp.set_errorstring(strError);
239 gpuBaseResp.set_lowlevelerror(err);
242 gpuBaseResp.set_embeddedresponse(strRespInner);
244 gpuBaseResp.SerializeToString(pstrResp);
253 // sessionid - unused here, but the intent is that if there's any state associated with this particular request that needs to be maintained serverside
254 // that this could be used to indicate which of potentially many sets of these states should be used. Feel free to ignore if not needed.
255 // strReq - a serialized protobuf string (std::string) that contains the parameters to the request. The idea is that you would ParseFromString this
256 // parameter into your request structure (defined in your .proto file) and then pull parameters as needed.
257 // strResp - a serialized protobuf string (std::string) that contains the response data from this request. The idea is that you would set all the response
258 // values into your response protobuf (defined in your .proto file) and then serialize that structure to a std::string and return it from here.
259 // perrno - this is any service specific error number that might need to be returned as part of this specific method/interface.
260 // pstrError - along with perrno, there may be a human-readable string that can be returned. This is where you might return it.
263 // RPCEC - if all goes well with parsing and generating the protobufs or any network related stuff, then this should be rpcecNone. Otherwise if there's a
264 // a problem with these areas, then an appropriate RPCEC error should be returned here (see rpcec.h).
266 RPCEC PingPB(int /*sessionid*/, std::string strReq, std::string *pstrResp, int * /*perrno*/, std::string * /*pstrError*/)
268 RPCEC rpcec = rpcecNone;
269 gpureplaycontrolpb::PingReqPB gpuPingReq;
270 gpureplaycontrolpb::PingRespPB gpuPingResp;
274 gpuPingReq.ParseFromString(strReq);
276 ping = gpuPingReq.ping();
278 if (ping == g_ping_value)
279 gpuPingResp.set_message((ping ? "Same ping value as last time: TRUE" : "Same ping value as last time: FALSE"));
281 gpuPingResp.set_message((ping ? "Different ping value as last time: FALSE->TRUE" : "Different ping value as last time: TRUE->FALSE"));
285 gpuPingResp.SerializeToString(pstrResp);
292 // Log Callback Globals
293 static bool g_fLogCallbackEnabled = false;
294 static int g_cRegisteredCallbacks = 0;
295 static int g_logCallbackPort = LOGSTREAM_PORT;
296 static network::channel *g_pChannelLogCallBack = NULL;
298 void *InvokeLogListener(void *arg);
299 bool log_output_func(vogl::eConsoleMessageType type, const char *pMsg, void *pData);
301 typedef struct _regtype
309 static REGTYPE g_registrations[MAX_REGISTRATIONS] = { { SESSIONID_NONE, 0, 0, LT_INVALID } };
312 // RegisterLogCallbackPB
315 // sessionid - unused here, but the intent is that if there's any state associated with this particular request that needs to be maintained serverside
316 // that this could be used to indicate which of potentially many sets of these states should be used. Feel free to ignore if not needed.
317 // strReq - a serialized protobuf string (std::string) that contains the parameters to the request. The idea is that you would ParseFromString this
318 // parameter into your request structure (defined in your .proto file) and then pull parameters as needed.
319 // strResp - a serialized protobuf string (std::string) that contains the response data from this request. The idea is that you would set all the response
320 // values into your response protobuf (defined in your .proto file) and then serialize that structure to a std::string and return it from here.
321 // perrno - this is any service specific error number that might need to be returned as part of this specific method/interface.
322 // pstrError - along with perrno, there may be a human-readable string that can be returned. This is where you might return it.
325 // RPCEC - if all goes well with parsing and generating the protobufs or any network related stuff, then this should be rpcecNone. Otherwise if there's a
326 // a problem with these areas, then an appropriate RPCEC error should be returned here (see rpcec.h).
328 RPCEC RegisterLogCallbackPB(int /*sessionid*/, std::string strReq, std::string *pstrResp, int *perrno, std::string *pstrError)
331 // Registering and Deregistering these logging callbacks always happen on the same thread. So any global that only these two functions use will be safe
334 RPCEC rpcec = rpcecNone;
335 gpucontrolpb::RegisterLogCallbackReqPB gpuRegisterLogCallbackReq;
336 gpucontrolpb::RegisterLogCallbackRespPB gpuRegisterLogCallbackResp;
338 LOGTYPE ltReq = LT_INVALID;
339 int registrationid = REGISTRATION_INVALID; // Invalid
340 int registrationindex = MAX_REGISTRATIONS + 1;
344 gpuRegisterLogCallbackReq.ParseFromString(strReq);
347 // Register log callback
348 // typedef bool (*console_output_func)(eConsoleMessageType type, const char* pMsg, void* pData);
349 // vogl::console::add_console_output_func(LogCallbackFunc, NULL);
351 ltReq = (LOGTYPE)gpuRegisterLogCallbackReq.logfilter();
352 if (LT_INVALID == ltReq)
354 syslog(RAD_ERROR, "GLI RegisterLogCallbackPB - invalid log filter (LT_INVALID) requested\n");
356 *pstrError = "GLI RegisterLogCallbackPB - invalid log filter (LT_INVALID) requested";
363 // Find the next available slot to register our filter for this registration
364 for (int i = 0; i < MAX_REGISTRATIONS; i++)
366 if (LT_INVALID == g_registrations[i].ltFilter)
368 registrationid = rand() + 1; // Not really random enough...
369 registrationindex = i;
374 if (MAX_REGISTRATIONS + 1 == registrationindex)
376 // Too many registrations!
378 *pstrError = "GLI RegisterLogCallbackPB - too many registrations already. Deregister some and try again.";
384 // Check to see if we need a connection
386 if (!g_fLogCallbackEnabled)
388 // Spawn a thread to be connected to
389 pthread_t threadLogging;
391 syslog(RAD_INFO, "Spinning up thread for GLI Trace logging...\n");
393 // Launching a GLI Listener
394 err = pthread_create(&threadLogging, NULL, InvokeLogListener, NULL); // &port??
397 syslog(RAD_ERROR, "Unable to create GLI Logging thread. returned %d, errno %d\n", err, errno);
399 *pstrError = "GLI RegisterLogCallbackPB - Unable to create GLI Logging thread. See system log for more info.";
404 // Register log callback - we only have one of these and we get rid of it when
405 // we have no more registrations.
407 // typedef bool (*console_output_func)(eConsoleMessageType type, const char* pMsg, void* pData);
408 // vogl::console::add_console_output_func(LogCallbackFunc, NULL);
409 vogl::console::add_console_output_func(log_output_func, NULL);
412 g_registrations[registrationindex].ltFilter = ltReq;
413 g_registrations[registrationindex].registration = registrationid;
414 g_cRegisteredCallbacks++;
416 gpuRegisterLogCallbackResp.set_registrationid(registrationid);
419 gpuRegisterLogCallbackResp.SerializeToString(pstrResp);
425 // DeregisterLogCallbackPB
428 // sessionid - unused here, but the intent is that if there's any state associated with this particular request that needs to be maintained serverside
429 // that this could be used to indicate which of potentially many sets of these states should be used. Feel free to ignore if not needed.
430 // strReq - a serialized protobuf string (std::string) that contains the parameters to the request. The idea is that you would ParseFromString this
431 // parameter into your request structure (defined in your .proto file) and then pull parameters as needed.
432 // strResp - a serialized protobuf string (std::string) that contains the response data from this request. The idea is that you would set all the response
433 // values into your response protobuf (defined in your .proto file) and then serialize that structure to a std::string and return it from here.
434 // perrno - this is any service specific error number that might need to be returned as part of this specific method/interface.
435 // pstrError - along with perrno, there may be a human-readable string that can be returned. This is where you might return it.
438 // RPCEC - if all goes well with parsing and generating the protobufs or any network related stuff, then this should be rpcecNone. Otherwise if there's a
439 // a problem with these areas, then an appropriate RPCEC error should be returned here (see rpcec.h).
441 RPCEC DeregisterLogCallbackPB(int /*sessionid*/, std::string strReq, std::string *pstrResp, int *perrno, std::string *pstrError)
443 RPCEC rpcec = rpcecNone;
444 int registrationid = REGISTRATION_INVALID; // Invalid
445 int registrationindex = MAX_REGISTRATIONS + 1;
447 gpucontrolpb::DeregisterLogCallbackReqPB gpuDeregisterLogCallbackReq;
448 gpucontrolpb::DeregisterLogCallbackRespPB gpuDeregisterLogCallbackResp;
450 gpuDeregisterLogCallbackReq.ParseFromString(strReq);
452 registrationid = gpuDeregisterLogCallbackReq.registrationid();
455 // Find the registrationid in the list
457 for (int i = 0; i < MAX_REGISTRATIONS; i++)
459 if (registrationid == g_registrations[i].registration)
461 registrationindex = i;
466 if (MAX_REGISTRATIONS <= registrationindex)
470 *pstrError = "GLI DeregisterLogCallbackPB - Unknown registration id(1).";
476 if (LT_INVALID == g_registrations[registrationindex].ltFilter)
478 // Invalid registrationid
480 *pstrError = "GLI DeregisterLogCallbackPB - Unknown registration id(2).";
485 g_registrations[registrationindex].ltFilter = LT_INVALID;
486 g_registrations[registrationindex].registration = REGISTRATION_INVALID;
488 g_cRegisteredCallbacks--;
489 if (0 == g_cRegisteredCallbacks)
491 vogl::console::remove_console_output_func(log_output_func);
492 g_fLogCallbackEnabled = false;
493 // Race condition? We could be sending a log on a different thread and then try to close out socket and descroy the context here at
496 g_pChannelLogCallBack->Disconnect();
497 delete g_pChannelLogCallBack;
498 g_pChannelLogCallBack = NULL;
502 gpuDeregisterLogCallbackResp.SerializeToString(pstrResp);
508 InvokeLogListener(void * /*arg*/)
511 // Setup to wait for a client to connect
513 pthread_t thisThread = pthread_self();
514 network::CHEC chec = network::EC_NONE;
516 g_pChannelLogCallBack = new network::channel();
518 chec = g_pChannelLogCallBack->Connect(g_logCallbackPort, 3, true);
519 if (network::EC_NONE != chec)
521 g_pChannelLogCallBack->Disconnect();
522 delete g_pChannelLogCallBack;
523 g_pChannelLogCallBack = NULL;
525 syslog(RAD_ERROR, "%lx: InvokeLogListener: Error on pChannel::Connect(port = %d, errno = %x): %x\nTerminating...\n", thisThread, g_logCallbackPort, errno, chec);
529 g_fLogCallbackEnabled = true;
535 LOGTYPE crnMsgTypeToLogType(vogl::eConsoleMessageType type);
536 RPCEC EcSendNotif(network::channel *pChannel, int notifid, int sessionid, std::string strLogMsg);
538 bool log_output_func(vogl::eConsoleMessageType type, const char *pMsg, void * /*pData*/)
540 // NOTE: This could be called from any thread... We're not 100% safe.
541 pthread_t thisThread = pthread_self();
542 LOGTYPE ltMessage = LT_INVALID;
543 RPCEC rpcec = rpcecNone;
545 gpucontrolpb::LogCallbackPB logMessagePB;
546 std::string strLogMsg;
548 bool fFoundAtLeastOneMatch = false;
549 int registrationid = REGISTRATION_INVALID;
551 if (!g_fLogCallbackEnabled)
553 // No one is ready to receive this data. Dump the pMsg.
558 // Filter out those messages that we're not looking for
559 ltMessage = crnMsgTypeToLogType(type);
561 // Loop through all our registrations and for those that are interested in
562 // this message, add them to the list of registrations this matches
564 for (int i = 0; i < MAX_REGISTRATIONS; i++)
566 if ((ltMessage & g_registrations[i].ltFilter))
568 if (REGISTRATION_INVALID != g_registrations[i].registration)
570 fFoundAtLeastOneMatch = true;
572 registrationid = g_registrations[i].registration;
574 logMessagePB.add_sessionids(g_registrations[i].sessionid);
575 logMessagePB.add_registrationids(registrationid);
580 if (!fFoundAtLeastOneMatch)
582 // Nothing matches the filter...drop the pMsg.
587 logMessagePB.set_logtype(ltMessage);
589 logMessagePB.set_message(pMsg);
591 logMessagePB.SerializeToString(&strLogMsg);
596 // We have a list of sessionids in the actual message...no need to choose one here.
597 rpcec = EcSendNotif(g_pChannelLogCallBack, gpucontrolpb::LOGCALLBACK, SESSIONID_NONE, strLogMsg);
599 if (rpcecNone != rpcec)
601 syslog(RAD_ERROR, "%lx: log_output_func(id=%d) EcSendResponse returns %x. Message \"%s\" unsent.\n", thisThread, registrationid, rpcec, pMsg);
610 RPCEC EcSendNotif(network::channel *pChannel, int notifid, int sessionid, std::string strLogMsg)
612 RPCEC rpcec = rpcecNone;
613 // Wraps the strLogMsg in the appropriate
614 debugserverpb::BaseMethodNotifPB gpuNotifPB;
615 std::string strNotifMsg;
617 gpuNotifPB.set_notifid(notifid);
618 gpuNotifPB.set_dbgsessionid(sessionid);
620 gpuNotifPB.set_embeddednotif(strLogMsg);
622 gpuNotifPB.SerializeToString(&strNotifMsg);
624 rpcec = EcSendResponse(pChannel, strNotifMsg);
629 LOGTYPE crnMsgTypeToLogType(vogl::eConsoleMessageType type)
634 case vogl::cDebugConsoleMessage:
638 case vogl::cProgressConsoleMessage:
642 case vogl::cInfoConsoleMessage:
646 case vogl::cConsoleConsoleMessage:
650 case vogl::cMessageConsoleMessage:
654 case vogl::cWarningConsoleMessage:
658 case vogl::cErrorConsoleMessage:
668 return LT_LOG; // Not sure what else to do here...?
673 rpcecFromChec(network::CHEC chec)
677 case network::EC_NONE:
681 case network::EC_TIMEOUT:
685 case network::EC_NETWORK:
689 case network::EC_MEMORY:
701 #endif // VOGL_REMOTING