]> git.cworth.org Git - apitrace/blob - common/trace_writer_local.cpp
common/backtrace: move namespace trace -> os
[apitrace] / common / trace_writer_local.cpp
1 /**************************************************************************
2  *
3  * Copyright 2007-2011 VMware, Inc.
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 #include <assert.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "os.hpp"
34 #include "os_thread.hpp"
35 #include "os_string.hpp"
36 #include "trace_file.hpp"
37 #include "trace_writer_local.hpp"
38 #include "trace_format.hpp"
39 #include "os_backtrace.hpp"
40
41
42 namespace trace {
43
44
45 static const char *memcpy_args[3] = {"dest", "src", "n"};
46 const FunctionSig memcpy_sig = {0, "memcpy", 3, memcpy_args};
47
48 static const char *malloc_args[1] = {"size"};
49 const FunctionSig malloc_sig = {1, "malloc", 1, malloc_args};
50
51 static const char *free_args[1] = {"ptr"};
52 const FunctionSig free_sig = {2, "free", 1, free_args};
53
54 static const char *realloc_args[2] = {"ptr", "size"};
55 const FunctionSig realloc_sig = {3, "realloc", 2, realloc_args};
56
57
58 static void exceptionCallback(void)
59 {
60     localWriter.flush();
61 }
62
63
64 LocalWriter::LocalWriter() :
65     acquired(0)
66 {
67     os::log("apitrace: loaded\n");
68
69     // Install the signal handlers as early as possible, to prevent
70     // interfering with the application's signal handling.
71     os::setExceptionCallback(exceptionCallback);
72 }
73
74 LocalWriter::~LocalWriter()
75 {
76     os::resetExceptionCallback();
77     checkProcessId();
78 }
79
80 void
81 LocalWriter::open(void) {
82     os::String szFileName;
83
84     const char *lpFileName;
85
86     lpFileName = getenv("TRACE_FILE");
87     if (!lpFileName) {
88         static unsigned dwCounter = 0;
89
90         os::String process = os::getProcessName();
91 #ifdef _WIN32
92         process.trimExtension();
93 #endif
94         process.trimDirectory();
95
96 #ifdef ANDROID
97         os::String prefix = "/data";
98 #else
99         os::String prefix = os::getCurrentDir();
100 #endif
101         prefix.join(process);
102
103         for (;;) {
104             FILE *file;
105
106             if (dwCounter)
107                 szFileName = os::String::format("%s.%u.trace", prefix.str(), dwCounter);
108             else
109                 szFileName = os::String::format("%s.trace", prefix.str());
110
111             lpFileName = szFileName;
112             file = fopen(lpFileName, "rb");
113             if (file == NULL)
114                 break;
115
116             fclose(file);
117
118             ++dwCounter;
119         }
120     }
121
122     os::log("apitrace: tracing to %s\n", lpFileName);
123
124     if (!Writer::open(lpFileName)) {
125         os::log("apitrace: error: failed to open %s\n", lpFileName);
126         os::abort();
127     }
128
129     pid = os::getCurrentProcessId();
130
131 #if 0
132     // For debugging the exception handler
133     *((int *)0) = 0;
134 #endif
135 }
136
137 static uintptr_t next_thread_num = 1;
138
139 static OS_THREAD_SPECIFIC_PTR(void)
140 thread_num;
141
142 void LocalWriter::checkProcessId(void) {
143     if (m_file->isOpened() &&
144         os::getCurrentProcessId() != pid) {
145         // We are a forked child process that inherited the trace file, so
146         // create a new file.  We can't call any method of the current
147         // file, as it may cause it to flush and corrupt the parent's
148         // trace, so we effectively leak the old file object.
149         m_file = File::createSnappy();
150         // Don't want to open the same file again
151         os::unsetEnvironment("TRACE_FILE");
152         open();
153     }
154 }
155
156 unsigned LocalWriter::beginEnter(const FunctionSig *sig, bool fake) {
157     mutex.lock();
158     ++acquired;
159
160     checkProcessId();
161     if (!m_file->isOpened()) {
162         open();
163     }
164
165     // Although thread_num is a void *, we actually use it as a uintptr_t
166     uintptr_t this_thread_num =
167         reinterpret_cast<uintptr_t>(static_cast<void *>(thread_num));
168     if (!this_thread_num) {
169         this_thread_num = next_thread_num++;
170         thread_num = reinterpret_cast<void *>(this_thread_num);
171     }
172
173     assert(this_thread_num);
174     unsigned thread_id = this_thread_num - 1;
175     unsigned call_no = Writer::beginEnter(sig, thread_id);
176     if (!fake && os::backtrace_is_needed(sig->name)) {
177         std::vector<RawStackFrame> backtrace = os::get_backtrace();
178         beginBacktrace(backtrace.size());
179         for (unsigned i = 0; i < backtrace.size(); ++i) {
180             writeStackFrame(&backtrace[i]);
181         }
182         endBacktrace();
183     }
184     return call_no;
185 }
186
187 void LocalWriter::endEnter(void) {
188     Writer::endEnter();
189     --acquired;
190     mutex.unlock();
191 }
192
193 void LocalWriter::beginLeave(unsigned call) {
194     mutex.lock();
195     ++acquired;
196     Writer::beginLeave(call);
197 }
198
199 void LocalWriter::endLeave(void) {
200     Writer::endLeave();
201     --acquired;
202     mutex.unlock();
203 }
204
205 void LocalWriter::flush(void) {
206     /*
207      * Do nothing if the mutex is already acquired (e.g., if a segfault happen
208      * while writing the file) as state could be inconsistent, therefore yield
209      * inconsistent trace files and/or repeated segfaults till infinity.
210      */
211
212     mutex.lock();
213     if (acquired) {
214         os::log("apitrace: ignoring exception while tracing\n");
215     } else {
216         ++acquired;
217         if (m_file->isOpened()) {
218             if (os::getCurrentProcessId() != pid) {
219                 os::log("apitrace: ignoring exception in child process\n");
220             } else {
221                 os::log("apitrace: flushing trace due to an exception\n");
222                 m_file->flush();
223             }
224         }
225         --acquired;
226     }
227     mutex.unlock();
228 }
229
230
231 LocalWriter localWriter;
232
233
234 } /* namespace trace */
235