]> git.cworth.org Git - vogl/blob - src/libtelemetry/libtelemetry.cpp
Initial vogl checkin
[vogl] / src / libtelemetry / libtelemetry.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 //
27 // libtelemetry.cpp
28 //
29 #include "libtelemetry.h"
30 #include "vogl_core.h"
31 #include "vogl_command_line_params.h"
32
33 #if !defined( USE_TELEMETRY )
34 #error "libtelemetry.cpp should not be built when USE_TELEMETRY is not defined."
35 #endif
36
37 telemetry_ctx_t g_tminfo;
38
39 // How much memory we want Telemetry to use. Mr. Hook says 2 megs is optimal.
40 static const size_t g_telemetry_arena_size = 2 * 1024 * 1024;
41
42 static struct
43 {
44     bool loaded;          // Telemetry is loaded?
45     HTELEMETRY ctx;       // Telemetry context.
46
47     int level;            // Telemetry level.
48     int new_level;        // New Telemetry level to set (on next telemetry_tick)
49     char servername[256]; // servername (defaults to localhost)
50     char appname[256];    // application name.
51     
52     // False if you want to use the release mode DLL or true if you want to
53     // use the checked DLL.  The checked DLL is compiled with optimizations but
54     // does extra run time checks and reporting.
55     bool use_checked_dll;
56     
57     TmU8 *mem_arena;      // allocated mem arena.
58 } g_tmdata;
59
60 /*
61  * Initialize Telemetry
62  */
63 static bool
64 telemetry_initialize()
65 {
66         TmErrorCode retval;
67
68         if (g_tmdata.ctx)
69         {
70         // If we've already got a context, check if it's connected.
71                 TmConnectionStatus status = tmGetConnectionStatus(g_tmdata.ctx);
72                 if (status == TMCS_CONNECTED || status == TMCS_CONNECTING)
73                         return true;
74         }
75
76         if (!g_tmdata.loaded)
77         {
78                 /* int load_telemetry = */ tmLoadTelemetry( g_tmdata.use_checked_dll );
79
80                 retval = tmStartup();
81                 if (retval != TM_OK)
82                 {
83             // Warning( "TelemetryInit() failed: tmStartup() returned %d, tmLoadTelemetry() returned %d.\n", retval, load_telemetry );
84                         return false;
85                 }
86
87                 if (!g_tmdata.mem_arena)
88                 {
89             // Allocate mem arena.
90                         g_tmdata.mem_arena = new TmU8[g_telemetry_arena_size];
91                 }
92
93         // Initialize our context.
94                 retval = tmInitializeContext(&g_tmdata.ctx, g_tmdata.mem_arena, g_telemetry_arena_size);
95                 if ( retval != TM_OK )
96                 {
97                         delete [] g_tmdata.mem_arena;
98                         g_tmdata.mem_arena = NULL;
99
100             // Warning( "TelemetryInit() failed: tmInitializeContext() returned %d.\n", retval );
101                         return false;
102                 }
103
104                 g_tmdata.loaded = true;
105         }
106
107         const char *app_name = telemetry_get_appname();
108         const char *server_address = telemetry_get_servername();
109         TmConnectionType server_type = TMCT_TCP;
110
111         char build_info[2048];
112     vogl::dynamic_string cmdline = vogl::get_command_line();
113         snprintf( build_info, sizeof( build_info ), "%s: %s", __DATE__ __TIME__, cmdline.c_str() );
114         build_info[ sizeof( build_info ) - 1 ] = 0;
115
116         TmU32 open_flags = TMOF_DEFAULT | TMOF_MINIMAL_CONTEXT_SWITCHES;
117         /* open_flags |= TMOF_DISABLE_CONTEXT_SWITCHES | TMOF_INIT_NETWORKING*/
118
119         retval = tmOpen( g_tmdata.ctx, app_name, build_info, server_address, server_type,
120                 TELEMETRY_DEFAULT_PORT, open_flags, 1000 );
121         if ( retval != TM_OK )
122         {
123         // Warning( "TelemetryInitialize() failed: tmOpen returned %d.\n", retval );
124                 return false;
125         }
126
127         // printf( "Telemetry initialized at level %u.\n", g_tmdata.level );
128         return true;
129 }
130
131 /*
132  * Shutdown Telemetry
133  */
134 static void
135 telemetry_shutdown()
136 {
137     HTELEMETRY ctx = g_tmdata.ctx;
138
139         if (ctx)
140         {
141                 TmConnectionStatus status = tmGetConnectionStatus(ctx);
142                 if (status == TMCS_CONNECTED || status == TMCS_CONNECTING)
143                         tmClose(ctx);
144
145         // Clear all our global ctx levels.
146                 memset(get_tminfo().ctx, 0, sizeof(get_tminfo().ctx));
147                 g_tmdata.ctx = NULL;
148
149         // Sleep for one second and hopefully all the background threads will be done
150         // with their contexts? If not, we'll most likely crash. Not sure what else we
151         // can do here other than just pause telemetry perhaps?
152         sleep(1);
153
154                 tmShutdownContext( ctx ); 
155
156         delete [] g_tmdata.mem_arena;
157         g_tmdata.mem_arena = NULL;
158
159                 tmShutdown();
160                 g_tmdata.loaded = false;
161         }
162 }
163
164 class CTelemetryShutdown
165 {
166 public:
167         CTelemetryShutdown()    {}
168         ~CTelemetryShutdown()   { telemetry_shutdown(); }
169 } g_TelemetryShutdown;
170
171 SO_API_EXPORT void
172 telemetry_tick()
173 {
174         if (g_tmdata.ctx)
175         {
176                 tmTick(g_tmdata.ctx);
177         }
178
179     // If the level has changed, reinit things.
180         if (g_tmdata.level != g_tmdata.new_level)
181         {
182         static const int max_levels = (int)(sizeof(get_tminfo().ctx) / sizeof(get_tminfo().ctx[0]));
183         if (g_tmdata.new_level > max_levels)
184             g_tmdata.new_level = max_levels;
185
186                 g_tmdata.level = g_tmdata.new_level;
187                 memset(get_tminfo().ctx, 0, sizeof(get_tminfo().ctx));
188
189                 if (g_tmdata.level == -1)
190                 {
191             // Should we just pause or try to shutdown entirely?
192             //  Try shutdown for now...
193             // tmPause(g_tmdata.ctx, 1);
194                         telemetry_shutdown();
195                 }
196                 else
197                 {
198                         if (!telemetry_initialize())
199                         {
200                                 g_tmdata.level = -1;
201                         }
202                         else
203                         {
204                                 tmPause(g_tmdata.ctx, 0);
205
206                 for (int i = 0; i <= g_tmdata.level; i++)
207                 {
208                     get_tminfo().ctx[i] = g_tmdata.ctx;
209                                 }
210                         }
211                 }
212
213         g_tmdata.new_level = g_tmdata.level;
214         }
215 }
216
217 SO_API_EXPORT const char *
218 telemetry_get_servername()
219 {
220     if (!g_tmdata.servername[0])
221         return "localhost";
222     return g_tmdata.servername;
223 }
224
225 SO_API_EXPORT void 
226 telemetry_set_servername(const char *servername)
227 {
228     if (servername)
229         strncpy(g_tmdata.servername, servername, sizeof(g_tmdata.servername));
230     else
231         g_tmdata.servername[0] = 0;
232 }
233
234 SO_API_EXPORT const char *
235 telemetry_get_appname()
236 {
237     if (!g_tmdata.appname[0])
238     {
239         vogl::dynamic_string_array params(vogl::get_command_line_params());
240         if (params.size())
241         {
242             char *appname = basename(params[0].c_str());
243             if (appname)
244                 strncpy(g_tmdata.appname, appname, sizeof(g_tmdata.appname));
245         }
246     }
247     if (!g_tmdata.appname[0])
248         return "AppName";
249     return g_tmdata.appname;
250 }
251
252 SO_API_EXPORT void 
253 telemetry_set_appname(const char *appname)
254 {
255     if (appname)
256         strncpy(g_tmdata.appname, appname, sizeof(g_tmdata.appname));
257     else
258         g_tmdata.appname[0] = 0;
259 }
260
261 SO_API_EXPORT int
262 telemetry_get_level()
263 {
264     // If we've never loaded telemetry, our level is -1.
265     if (!g_tmdata.loaded)
266         g_tmdata.level = -1;
267
268     return g_tmdata.level;
269 }
270
271 SO_API_EXPORT void
272 telemetry_set_level(int level)
273 {
274     // If we've never loaded telemetry, our level is -1.
275     if (!g_tmdata.loaded)
276         g_tmdata.level = -1;
277
278     // Set our new level.
279     g_tmdata.new_level = level;
280 }