Thomas Sondergaard's API tracer
[mesa.git] / progs / tools / trace / gltrace_support.cc
1 /*
2 * Copyright (C) 2006 Thomas Sondergaard All Rights Reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 */
21
22 #include "gltrace_support.h"
23 #include <sstream>
24 #include <fstream>
25 #include <iomanip>
26 #include <execinfo.h>
27 #include <cxxabi.h>
28 #include <sys/time.h>
29
30 namespace {
31
32 const char *
33 demangle (const char * mangled) throw()
34 {
35 static char buf[4096];
36 int status;
37 unsigned int length = sizeof(buf)-1;
38
39 memset (buf, 0, sizeof(buf));
40
41 if (!mangled)
42 return 0;
43
44 char * demangled = __cxxabiv1::__cxa_demangle(mangled,
45 buf,
46 &length,
47 &status);
48 if (demangled && !status)
49 return demangled;
50 else
51 return mangled;
52 }
53
54 void
55 printStackTrace (void **stackframes,
56 int stackframe_size,
57 std::ostream & out )
58 {
59 char **strings = 0;
60 std::stringstream ss;
61
62 // this might actually fail if memory is tight or we are in a
63 // signal handler
64 strings = backtrace_symbols (stackframes, stackframe_size);
65
66 ss << "Backtrace :";
67
68 if (stackframe_size == gltrace::MAX_STACKFRAMES)
69 ss << "(possibly incomplete maximal number of frames exceeded):" << std::endl;
70 else
71 ss << std::endl;
72
73 out << ss.str();
74
75 // the first frame is the constructor of the exception
76 // the last frame always seem to be bogus?
77 for (int i = 0; strings && i < stackframe_size-1; ++i) {
78 char libname[257], funcname[2049];
79 unsigned int address=0, funcoffset = 0x0;
80
81 memset (libname,0,sizeof(libname));
82 memset (funcname,0,sizeof(funcname));
83
84 strcpy (funcname,"??");
85 strcpy (libname, "??");
86
87 int scanned = sscanf (strings[i], "%256[^(] ( %2048[^+] + %x ) [ %x ]",
88 libname,
89 funcname,
90 &funcoffset,
91 &address);
92
93 /* ok, so no function was mentioned in the backtrace */
94 if (scanned < 4) {
95 scanned = sscanf (strings[i], "%256[^([] [ %x ]",
96 libname,
97 &address);
98 }
99
100 if (funcname[0] == '_') {
101 const char * demangled;
102 if ((demangled = demangle(funcname) ) != funcname) {
103 strncpy (funcname, demangled, sizeof(funcname)-1);
104 }
105 }
106 else
107 strcat (funcname," ()");
108
109 out << "\t#" << i << std::hex << " 0x" << address << " in " << funcname
110 << " at 0x" << funcoffset << " (from " << libname << ")" << std::endl;
111 }
112
113 free (strings);
114 }
115
116
117 } // anon namespace
118
119 namespace gltrace {
120
121 std::string getStackTrace(int count, int first) {
122 ++first;
123 std::stringstream ss;
124 const int BA_MAX = 1000;
125 assert(count + first <= BA_MAX);
126 void *ba[BA_MAX];
127 int n = backtrace(ba, count+first);
128
129 printStackTrace( &ba[first], n-first, ss);
130
131 return ss.str();
132 }
133
134 std::ostream &timeNow(std::ostream &os) {
135
136 struct timeval now;
137 struct tm t;
138 static char *months[12] =
139 {
140 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
141 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
142 };
143
144 gettimeofday (&now, 0);
145 localtime_r ((time_t*) &now.tv_sec, &t);
146
147 os
148 << months[t.tm_mon] << " "
149 << std::setw(2) << t.tm_mday << " "
150 << std::setw(2) << t.tm_hour << ":"
151 << std::setw(2) << t.tm_min << ":"
152 << std::setw(2) << t.tm_sec << "."
153 << std::setw(3) << now.tv_usec/1000;
154 return os;
155 }
156
157 logstream::logstream(const char *filename) {
158 if (!filename)
159 init(std::cerr.rdbuf());
160 else {
161 file_os.reset(new std::ofstream(filename));
162 if (file_os->good())
163 init(file_os->rdbuf());
164 else {
165 std::cerr << "ERROR: gltrace: Failed to open '" << filename
166 << "' for writing. Falling back to stderr." << std::endl;
167 init(std::cerr.rdbuf());
168 }
169 }
170 *this << std::setfill('0'); // setw used in timeNow
171 }
172
173
174 Config::Config() :
175 logCalls(true),
176 checkErrors(true),
177 logTime(true),
178 log(getenv("GLTRACE_LOGFILE")) {
179 if (const char *v = getenv("GLTRACE_LOG_CALLS"))
180 logCalls = strncmp("1", v, 1) == 0;
181 if (const char *v = getenv("GLTRACE_CHECK_ERRORS"))
182 checkErrors = strncmp("1", v, 1) == 0;
183 if (const char *v = getenv("GLTRACE_LOG_TIME"))
184 logTime = strncmp("1", v, 1) == 0;
185 }
186
187 // *The* config
188 Config config;
189
190 } // namespace gltrace