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 **************************************************************************/
26 // File: vogl_trace.cpp
27 #include "vogl_trace.h"
28 #include "vogl_command_line_params.h"
29 #include "vogl_colorized_console.h"
31 #include <linux/unistd.h>
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 //----------------------------------------------------------------------------------------------------------------------
42 #define VOGL_LIBGL_SO_FILENAME "libGL.so.1"
44 //----------------------------------------------------------------------------------------------------------------------
46 //----------------------------------------------------------------------------------------------------------------------
47 void *g_vogl_actual_libgl_module_handle;
48 bool g_vogl_initializing_flag;
50 //----------------------------------------------------------------------------------------------------------------------
52 //----------------------------------------------------------------------------------------------------------------------
53 typedef void *(*dlopen_func_ptr_t)(const char *pFile, int mode);
54 VOGL_API_EXPORT void *dlopen(const char *pFile, int mode)
56 static dlopen_func_ptr_t s_pActual_dlopen = (dlopen_func_ptr_t)dlsym(RTLD_NEXT, "dlopen");
57 if (!s_pActual_dlopen)
60 printf("(vogltrace) dlopen: %s %i\n", pFile ? pFile : "(nullptr)", mode);
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)
66 if (pFile && (strstr(pFile, "libGL.so") != NULL))
68 const char *calling_module = btrace_get_calling_module();
69 bool should_redirect = !strstr(calling_module, "fglrx");
73 pFile = btrace_get_current_module();
74 printf("(vogltrace) redirecting dlopen to %s\n", pFile);
78 printf("(vogltrace) NOT redirecting dlopen to %s, this dlopen() call appears to be coming from the driver\n", pFile);
81 printf("------------\n");
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);
90 // If this file hadn't been loaded before, notify btrace.
91 if (!is_loaded && dlopen_ret)
92 btrace_dlopen_notify(pFile);
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)
103 static exit_func_ptr_t s_pActual_exit = (exit_func_ptr_t)dlsym(RTLD_NEXT, "_exit");
108 (*s_pActual_exit)(status);
112 // to shut up compiler about this func marked as noreturn
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)
123 static Exit_func_ptr_t s_pActual_Exit = (Exit_func_ptr_t)dlsym(RTLD_NEXT, "_Exit");
128 (*s_pActual_Exit)(status);
132 // to shut up compiler about this func marked as noreturn
137 //----------------------------------------------------------------------------------------------------------------------
138 // vogl_glInternalTraceCommandRAD_dummy_func
139 //----------------------------------------------------------------------------------------------------------------------
140 static void vogl_glInternalTraceCommandRAD_dummy_func(GLuint cmd, GLuint size, const GLubyte *data)
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);
148 //----------------------------------------------------------------------------------------------------------------------
149 // vogl_get_proc_address_helper
150 //----------------------------------------------------------------------------------------------------------------------
151 static vogl_void_func_ptr_t vogl_get_proc_address_helper(const char *pName)
153 if (!strcmp(pName, "glInternalTraceCommandRAD"))
154 return reinterpret_cast<vogl_void_func_ptr_t>(vogl_glInternalTraceCommandRAD_dummy_func);
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));
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)));
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()
170 //printf("vogl_shared_object_constructor_func\n");
172 // Doesn't seem necessary to do this - our global constructor gets called earlier, just being safe.
175 g_vogl_initializing_flag = true;
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) ");
180 vogl_message_printf("%s built %s %s, begin initialization in %s\n", btrace_get_current_module(), __DATE__, __TIME__, getenv("_"));
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)
189 bool debugger_connected = false;
190 dynamic_string cmdline = vogl::get_command_line();
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");
200 debugger_connected = vogl_is_debugger_present(true);
201 if (debugger_connected || vogl_kbhit())
205 if (debugger_connected)
206 vogl_message_printf(" Debugger connected...\n");
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)
212 vogl_warning_printf("%s: dlsym(RTLD_NEXT, \"glXGetProcAddress\") failed, trying to manually load %s\n", VOGL_FUNCTION_NAME, VOGL_LIBGL_SO_FILENAME);
214 g_vogl_actual_libgl_module_handle = dlopen(VOGL_LIBGL_SO_FILENAME, RTLD_LAZY);
215 if (!g_vogl_actual_libgl_module_handle)
217 vogl_error_printf("%s: Failed loading %s!\n", VOGL_FUNCTION_NAME, VOGL_LIBGL_SO_FILENAME);
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)
224 vogl_error_printf("%s: Failed getting address of glXGetProcAddress() from %s!\n", VOGL_FUNCTION_NAME, VOGL_LIBGL_SO_FILENAME);
228 vogl_message_printf("%s: Manually loaded %s\n", VOGL_FUNCTION_NAME, VOGL_LIBGL_SO_FILENAME);
231 vogl_common_lib_early_init();
233 vogl_init_actual_gl_entrypoints(vogl_get_proc_address_helper);
237 vogl_message_printf("end initialization\n");
239 g_vogl_initializing_flag = false;
242 __attribute__((destructor)) static void vogl_shared_object_destructor_func()