]> git.cworth.org Git - vogl/blob - src/vogltrace/vogl_trace.cpp
Initial vogl checkin
[vogl] / src / vogltrace / vogl_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 // File: vogl_trace.cpp
27 #include "vogl_trace.h"
28 #include "vogl_command_line_params.h"
29 #include "vogl_colorized_console.h"
30 #include <signal.h>
31 #include <linux/unistd.h>
32 #include <execinfo.h>
33 #include "btrace.h"
34
35 //----------------------------------------------------------------------------------------------------------------------
36 // Has exception hooks, dlopen() interception, super low-level global init.  Be super careful what you
37 //  do in some of these funcs (particularly vogl_shared_object_constructor_func() or any of the funcs
38 //  it calls).  I've found it's possible to do shit here that will work fine in primitive apps (like
39 //  vogltest) but kill tracing in some real apps.
40 //----------------------------------------------------------------------------------------------------------------------
41
42 #define VOGL_LIBGL_SO_FILENAME "libGL.so.1"
43
44 //----------------------------------------------------------------------------------------------------------------------
45 // globals
46 //----------------------------------------------------------------------------------------------------------------------
47 void *g_vogl_actual_libgl_module_handle;
48 bool g_vogl_initializing_flag;
49
50 //----------------------------------------------------------------------------------------------------------------------
51 // dlopen interceptor
52 //----------------------------------------------------------------------------------------------------------------------
53 typedef void *(*dlopen_func_ptr_t)(const char *pFile, int mode);
54 VOGL_API_EXPORT void *dlopen(const char *pFile, int mode)
55 {
56     static dlopen_func_ptr_t s_pActual_dlopen = (dlopen_func_ptr_t)dlsym(RTLD_NEXT, "dlopen");
57     if (!s_pActual_dlopen)
58         return NULL;
59
60     printf("(vogltrace) dlopen: %s %i\n", pFile ? pFile : "(nullptr)", mode);
61
62     // Only redirect libGL.so when it comes from the app, NOT the driver or one of its associated helpers.
63     // This is definitely fragile as all fuck.
64     if (!g_vogl_initializing_flag)
65     {
66         if (pFile && (strstr(pFile, "libGL.so") != NULL))
67         {
68             const char *calling_module = btrace_get_calling_module();
69             bool should_redirect = !strstr(calling_module, "fglrx");
70
71             if (should_redirect)
72             {
73                 pFile = btrace_get_current_module();
74                 printf("(vogltrace) redirecting dlopen to %s\n", pFile);
75             }
76             else
77             {
78                 printf("(vogltrace) NOT redirecting dlopen to %s, this dlopen() call appears to be coming from the driver\n", pFile);
79             }
80
81             printf("------------\n");
82         }
83     }
84
85     // Check if this module is already loaded.
86     void *is_loaded = (*s_pActual_dlopen)(pFile, RTLD_NOLOAD);
87     // Call the real dlopen().
88     void *dlopen_ret = (*s_pActual_dlopen)(pFile, mode);
89
90     // If this file hadn't been loaded before, notify btrace.
91     if (!is_loaded && dlopen_ret)
92         btrace_dlopen_notify(pFile);
93
94     return dlopen_ret;
95 }
96
97 //----------------------------------------------------------------------------------------------------------------------
98 // Intercept _exit() so we get a final chance to flush our trace/log files
99 //----------------------------------------------------------------------------------------------------------------------
100 typedef void (*exit_func_ptr_t)(int status);
101 VOGL_API_EXPORT void _exit(int status)
102 {
103     static exit_func_ptr_t s_pActual_exit = (exit_func_ptr_t)dlsym(RTLD_NEXT, "_exit");
104
105     vogl_deinit();
106
107     if (s_pActual_exit)
108         (*s_pActual_exit)(status);
109
110     raise(SIGKILL);
111
112     // to shut up compiler about this func marked as noreturn
113     for (;;)
114         ;
115 }
116
117 //----------------------------------------------------------------------------------------------------------------------
118 // Intercept _exit() so we get a final chance to flush our trace/log files
119 //----------------------------------------------------------------------------------------------------------------------
120 typedef void (*Exit_func_ptr_t)(int status);
121 VOGL_API_EXPORT void _Exit(int status)
122 {
123     static Exit_func_ptr_t s_pActual_Exit = (Exit_func_ptr_t)dlsym(RTLD_NEXT, "_Exit");
124
125     vogl_deinit();
126
127     if (s_pActual_Exit)
128         (*s_pActual_Exit)(status);
129
130     raise(SIGKILL);
131
132     // to shut up compiler about this func marked as noreturn
133     for (;;)
134         ;
135 }
136
137 //----------------------------------------------------------------------------------------------------------------------
138 // vogl_glInternalTraceCommandRAD_dummy_func
139 //----------------------------------------------------------------------------------------------------------------------
140 static void vogl_glInternalTraceCommandRAD_dummy_func(GLuint cmd, GLuint size, const GLubyte *data)
141 {
142     // Nothing to do, this is an internal command used for serialization purposes that only we understand.
143     VOGL_NOTE_UNUSED(cmd);
144     VOGL_NOTE_UNUSED(size);
145     VOGL_NOTE_UNUSED(data);
146 }
147
148 //----------------------------------------------------------------------------------------------------------------------
149 // vogl_get_proc_address_helper
150 //----------------------------------------------------------------------------------------------------------------------
151 static vogl_void_func_ptr_t vogl_get_proc_address_helper(const char *pName)
152 {
153     if (!strcmp(pName, "glInternalTraceCommandRAD"))
154         return reinterpret_cast<vogl_void_func_ptr_t>(vogl_glInternalTraceCommandRAD_dummy_func);
155
156     vogl_void_func_ptr_t pFunc = reinterpret_cast<vogl_void_func_ptr_t>(dlsym(g_vogl_actual_libgl_module_handle ? g_vogl_actual_libgl_module_handle : RTLD_NEXT, pName));
157
158     if ((!pFunc) && (g_vogl_actual_gl_entrypoints.m_glXGetProcAddress))
159         pFunc = reinterpret_cast<vogl_void_func_ptr_t>(g_vogl_actual_gl_entrypoints.m_glXGetProcAddress(reinterpret_cast<const GLubyte *>(pName)));
160
161     return pFunc;
162 }
163
164 //----------------------------------------------------------------------------------------------------------------------
165 // global constructor init
166 // Note: Be VERY careful what you do in here! It's called very early during init (long before main, during c++ init)
167 //----------------------------------------------------------------------------------------------------------------------
168 __attribute__((constructor)) static void vogl_shared_object_constructor_func()
169 {
170     //printf("vogl_shared_object_constructor_func\n");
171
172     // Doesn't seem necessary to do this - our global constructor gets called earlier, just being safe.
173     vogl_init_heap();
174
175     g_vogl_initializing_flag = true;
176
177     // can't call vogl::colorized_console::init() because its global arrays will be cleared after this func returns
178     vogl::console::set_tool_prefix("(vogltrace) ");
179
180     vogl_message_printf("%s built %s %s, begin initialization in %s\n", btrace_get_current_module(), __DATE__, __TIME__, getenv("_"));
181
182     // We can't use the regular cmd line parser here because this func is called before global objects are constructed.
183     char *pEnv_cmd_line = getenv("VOGL_CMD_LINE");
184     bool pause = vogl::check_for_command_line_param("-vogl_pause") || ((pEnv_cmd_line) && (strstr(pEnv_cmd_line, "-vogl_pause") != NULL));
185     bool long_pause = vogl::check_for_command_line_param("-vogl_long_pause") || ((pEnv_cmd_line) && (strstr(pEnv_cmd_line, "-vogl_long_pause") != NULL));
186     if (pause || long_pause)
187     {
188         int count = 60000;
189         bool debugger_connected = false;
190         dynamic_string cmdline = vogl::get_command_line();
191
192         vogl_message_printf("cmdline: %s\n", cmdline.c_str());
193         vogl_message_printf("Pausing %d ms or until debugger is attached (pid %d).\n", count, getpid());
194         vogl_message_printf("  Or press any key to continue.\n");
195
196         while (count >= 0)
197         {
198             vogl_sleep(200);
199             count -= 200;
200             debugger_connected = vogl_is_debugger_present(true);
201             if (debugger_connected || vogl_kbhit())
202                 break;
203         }
204
205         if (debugger_connected)
206             vogl_message_printf("  Debugger connected...\n");
207     }
208
209     g_vogl_actual_gl_entrypoints.m_glXGetProcAddress = reinterpret_cast<glXGetProcAddress_func_ptr_t>(dlsym(RTLD_NEXT, "glXGetProcAddress"));
210     if (!g_vogl_actual_gl_entrypoints.m_glXGetProcAddress)
211     {
212         vogl_warning_printf("%s: dlsym(RTLD_NEXT, \"glXGetProcAddress\") failed, trying to manually load %s\n", VOGL_FUNCTION_NAME, VOGL_LIBGL_SO_FILENAME);
213
214         g_vogl_actual_libgl_module_handle = dlopen(VOGL_LIBGL_SO_FILENAME, RTLD_LAZY);
215         if (!g_vogl_actual_libgl_module_handle)
216         {
217             vogl_error_printf("%s: Failed loading %s!\n", VOGL_FUNCTION_NAME, VOGL_LIBGL_SO_FILENAME);
218             exit(EXIT_FAILURE);
219         }
220
221         g_vogl_actual_gl_entrypoints.m_glXGetProcAddress = reinterpret_cast<glXGetProcAddress_func_ptr_t>(dlsym(g_vogl_actual_libgl_module_handle, "glXGetProcAddress"));
222         if (!g_vogl_actual_gl_entrypoints.m_glXGetProcAddress)
223         {
224             vogl_error_printf("%s: Failed getting address of glXGetProcAddress() from %s!\n", VOGL_FUNCTION_NAME, VOGL_LIBGL_SO_FILENAME);
225             exit(EXIT_FAILURE);
226         }
227
228         vogl_message_printf("%s: Manually loaded %s\n", VOGL_FUNCTION_NAME, VOGL_LIBGL_SO_FILENAME);
229     }
230
231     vogl_common_lib_early_init();
232
233     vogl_init_actual_gl_entrypoints(vogl_get_proc_address_helper);
234
235     vogl_early_init();
236
237     vogl_message_printf("end initialization\n");
238
239     g_vogl_initializing_flag = false;
240 }
241
242 __attribute__((destructor)) static void vogl_shared_object_destructor_func()
243 {
244     vogl_deinit();
245 }