1 /* Copyright (c) 2008-2010, Google Inc.
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Neither the name of Google Inc. nor the names of its
11 * contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 // This file is part of ThreadSanitizer, a dynamic data race detector.
28 // Author: Konstantin Serebryany.
29 // Author: Timur Iskhodzhanov.
30
31 // Experimental off-line race detector.
32 // Reads program events from a file and detects races.
33 // See http://code.google.com/p/data-race-test
34
35 // ------------- Includes ------------- {{{1
36 #include "thread_sanitizer.h"
37 #include "ts_events.h"
38
39 #include <stdio.h>
40 #include <stdarg.h>
41 #include <ctype.h>
42 #include <time.h>
43
44 // ------------- Globals ------------- {{{1
45 static map<string, int> *g_event_type_map;
46 struct PcInfo {
47 string img_name;
48 string file_name;
49 string rtn_name;
50 int line;
51 };
52
53 static map<uintptr_t, PcInfo> *g_pc_info_map;
54
55 unsigned long offline_line_n;
56 //------------- Read binary file Utils ------------ {{{1
57 static const int kBufSize = 65536;
58
59 template<typename T>
Read(FILE * fp,T * res)60 static bool Read(FILE *fp, T *res) {
61 unsigned char buf[16];
62 int size = fread(buf, sizeof(T), 1, fp);
63 *res = 0;
64 for (unsigned int i=0; i<sizeof(T); i++) {
65 *res <<= 8;
66 *res += buf[i];
67 }
68 return size == 1;
69 }
70
71
ReadANSI(FILE * file,string * res)72 static bool ReadANSI(FILE *file, string *res) {
73 char buf[kBufSize];
74 unsigned short length;
75 if (!Read<unsigned short>(file, &length)) {
76 return false;
77 }
78 int size = fread(buf, 1, (int)length, file);
79 buf[length] = 0;
80 *res = (char *)buf;
81 return size == length;
82 }
83 //------------- Utils ------------------- {{{1
EventNameToEventType(const char * name)84 static EventType EventNameToEventType(const char *name) {
85 map<string, int>::iterator it = g_event_type_map->find(name);
86 if (it == g_event_type_map->end()) {
87 Printf("Unknown event type: %s\n", name);
88 }
89 CHECK(it != g_event_type_map->end());
90 return (EventType)it->second;
91 }
92
InitEventTypeMap()93 static void InitEventTypeMap() {
94 g_event_type_map = new map<string, int>;
95 for (int i = 0; i < LAST_EVENT; i++) {
96 (*g_event_type_map)[kEventNames[i]] = i;
97 }
98 }
99
SkipCommentText(FILE * file)100 static void SkipCommentText(FILE *file) {
101 char buff[kBufSize];
102 int i = 0;
103 while (true) {
104 int c = fgetc(file);
105 if (c == EOF) break;
106 if (c == '\n') {
107 offline_line_n++;
108 break;
109 }
110 if (i < kBufSize - 1)
111 buff[i++] = c;
112 }
113 buff[i] = 0;
114 if (buff[0] == 'P' && buff[1] == 'C') {
115 char img[kBufSize];
116 char rtn[kBufSize];
117 char file[kBufSize];
118 int line = 0;
119 unsigned long pc = 0;
120 if (sscanf(buff, "PC %lx %s %s %s %d", (unsigned long*)&pc,
121 img, rtn, file, &line) == 5 &&
122 pc != 0) {
123 CHECK(g_pc_info_map);
124 PcInfo pc_info;
125 pc_info.img_name = img;
126 pc_info.rtn_name = rtn;
127 pc_info.file_name = file;
128 pc_info.line = line;
129 (*g_pc_info_map)[pc] = pc_info;
130 // Printf("***** PC %lx %s\n", pc, rtn);
131 }
132 }
133 if (buff[0] == '>') {
134 // Just print the rest of comment.
135 Printf("%s\n", buff + 2);
136 }
137 }
138
SkipWhiteSpaceAndComments(FILE * file)139 static void SkipWhiteSpaceAndComments(FILE *file) {
140 int c = 0;
141 while (true) {
142 c = fgetc(file);
143 if (c == EOF) return;
144 if (c == '#' || c == '=') {
145 SkipCommentText(file);
146 continue;
147 }
148 if (isspace(c)) continue;
149 break;
150 }
151 ungetc(c, file);
152 }
153
154 typedef bool (*EventReader)(FILE *, Event *);
155
ReadOneStrEventFromFile(FILE * file,Event * event)156 bool ReadOneStrEventFromFile(FILE *file, Event *event) {
157 CHECK(event);
158 char name[1024];
159 uint32_t tid;
160 unsigned long pc, a, info;
161 SkipWhiteSpaceAndComments(file);
162 offline_line_n++;
163 if (5 == fscanf(file, "%s%x%lx%lx%lx", name, &tid, &pc, &a, &info)) {
164 event->Init(EventNameToEventType(name), tid, pc, a, info);
165 return true;
166 }
167 return false;
168 }
169
ProcessCodePosition(FILE * input,int * pc,string * str)170 bool ProcessCodePosition(FILE *input, int *pc, string *str) {
171 bool ok = Read<int>(input, pc);
172 ok &= ReadANSI(input, str);
173 return ok;
174 }
175
ProcessMessage(FILE * input,string * str)176 bool ProcessMessage(FILE *input, string *str) {
177 return ReadANSI(input, str);
178 }
179
180 // Read information about event in format: [[[info] address] pc] tid.
ProcessEvent(FILE * input,EventType type,Event * event)181 bool ProcessEvent(FILE *input, EventType type, Event *event) {
182 bool ok = true;
183 unsigned short tid = 0;
184 int pc = 0;
185 int64_t address = 0;
186 unsigned short extra = 0;
187 // It's tricky switch without breaks.
188 switch (type) {
189 case THR_START:
190 ok &= Read<unsigned short>(input, &extra);
191 // fallthrough.
192 case READ:
193 case READER_LOCK:
194 case SIGNAL:
195 case THR_JOIN_AFTER:
196 case UNLOCK:
197 case WAIT:
198 case WRITE:
199 case WRITER_LOCK:
200 ok &= Read<int64_t>(input, &address);
201 // fallthrough.
202 case EXPECT_RACE_BEGIN:
203 case EXPECT_RACE_END:
204 case RTN_EXIT:
205 case SBLOCK_ENTER:
206 case STACK_TRACE:
207 case THR_END:
208 case THR_FIRST_INSN:
209 ok &= Read<int>(input, &pc);
210 // fallthrough.
211 case RTN_CALL:
212 ok &= Read<unsigned short>(input, &tid);
213 break;
214 default:
215 // read unsupported EventType.
216 Printf("Unsupported EventType %s %d\n", type, (int)type);
217 CHECK(false);
218 }
219 if (type == READ || type == WRITE) {
220 extra = 1;
221 }
222 event->Init(type, (int)tid, pc, address, (int)extra);
223 return ok;
224 }
225
ReadOneBinEventFromFile(FILE * input,Event * event)226 bool ReadOneBinEventFromFile(FILE *input, Event *event) {
227 CHECK(event);
228 bool ok = true;
229 EventType type;
230 unsigned char typeOrd;
231 int pc;
232 int line;
233 char rtn[kBufSize];
234 char file[kBufSize];
235 string str;
236 while (ok) {
237 offline_line_n++;
238 ok &= Read<unsigned char>(input, &typeOrd);
239 if (!ok) break;
240 type = (EventType)typeOrd;
241 switch (type) {
242 case PC_DESCRIPTION:
243 ok &= ProcessCodePosition(input, &pc, &str);
244 if (sscanf(str.c_str(), "%s %s %d", rtn, file, &line) == 3 && pc != 0) {
245 CHECK(g_pc_info_map);
246 PcInfo pc_info;
247 pc_info.img_name = "java";
248 pc_info.rtn_name = rtn;
249 pc_info.file_name = file;
250 pc_info.line = line;
251 (*g_pc_info_map)[pc] = pc_info;
252 }
253 break;
254 case PRINT_MESSAGE:
255 ok &= ProcessMessage(input, &str);
256 // Just print the rest of comment.
257 Printf("%s\n", str.c_str());
258 break;
259 default:
260 ok &= ProcessEvent(input, type, event);
261 return ok;
262 }
263 }
264 return false;
265 }
266
DecodeEventsFromFile(FILE * input,FILE * output)267 void DecodeEventsFromFile(FILE *input, FILE *output) {
268 offline_line_n = 0;
269 bool ok = true;
270 EventType type;
271 unsigned char typeOrd;
272 int pc;
273 string str;
274 Event event;
275 while (ok) {
276 ok &= Read<unsigned char>(input, &typeOrd);
277 if (!ok) break;
278 type = (EventType)typeOrd;
279 switch (type) {
280 case PC_DESCRIPTION:
281 ok &= ProcessCodePosition(input, &pc, &str);
282 fprintf(output, "#PC %x java %s\n", pc, str.c_str());
283 break;
284 case PRINT_MESSAGE:
285 ok &= ProcessMessage(input, &str);
286 fprintf(output, "#> %s\n", str.c_str());
287 break;
288 default:
289 ok &= ProcessEvent(input, type, &event);
290 fprintf(output, "%s %x %x %lx %lx\n", kEventNames[event.type()],
291 event.tid(), (unsigned int)event.pc(),
292 (long unsigned int)event.a(), (long unsigned int)event.info());
293 break;
294 }
295 offline_line_n++;
296 }
297 Printf("INFO: ThreadSanitizer write %ld lines.\n", offline_line_n);
298 }
299
300 static const uint32_t max_unknown_thread = 10000;
301
302 static bool known_threads[max_unknown_thread] = {};
303
ReadEventsFromFile(FILE * file,EventReader event_reader_cb)304 INLINE void ReadEventsFromFile(FILE *file, EventReader event_reader_cb) {
305 Event event;
306 uint64_t n_events = 0;
307 offline_line_n = 0;
308 while (event_reader_cb(file, &event)) {
309 //event.Print();
310 n_events++;
311 uint32_t tid = event.tid();
312 if (event.type() == THR_START && tid < max_unknown_thread) {
313 known_threads[tid] = true;
314 }
315 if (tid >= max_unknown_thread || known_threads[tid]) {
316 ThreadSanitizerHandleOneEvent(&event);
317 }
318 }
319 Printf("INFO: ThreadSanitizerOffline: %ld events read\n", n_events);
320 }
321 //------------- ThreadSanitizer exports ------------ {{{1
322
PcToStrings(uintptr_t pc,bool demangle,string * img_name,string * rtn_name,string * file_name,int * line_no)323 void PcToStrings(uintptr_t pc, bool demangle,
324 string *img_name, string *rtn_name,
325 string *file_name, int *line_no) {
326 if (g_pc_info_map->count(pc) == 0) {
327 *img_name = "";
328 *rtn_name = "";
329 *file_name = "";
330 *line_no = 0;
331 return;
332 }
333 PcInfo &info = (*g_pc_info_map)[pc];
334 *img_name = info.img_name;
335 *rtn_name = info.rtn_name;
336 *file_name = info.file_name;
337 *line_no = info.line;
338 if (*file_name == "unknown")
339 *file_name = "";
340 }
341
PcToRtnName(uintptr_t pc,bool demangle)342 string PcToRtnName(uintptr_t pc, bool demangle) {
343 string img, rtn, file;
344 int line;
345 PcToStrings(pc, demangle, &img, &rtn, &file, &line);
346 return rtn;
347 }
348 //------------- main ---------------------------- {{{1
main(int argc,char * argv[])349 int main(int argc, char *argv[]) {
350 Printf("INFO: ThreadSanitizerOffline r%s\n", TS_VERSION);
351
352 InitEventTypeMap();
353 g_pc_info_map = new map<uintptr_t, PcInfo>;
354 G_flags = new FLAGS;
355
356 vector<string> args(argv + 1, argv + argc);
357 ThreadSanitizerParseFlags(&args);
358 ThreadSanitizerInit();
359
360 CHECK(G_flags);
361 if (G_flags->input_type == "bin") {
362 ReadEventsFromFile(stdin, ReadOneBinEventFromFile);
363 } else if (G_flags->input_type == "decode") {
364 FILE* output;
365 if (G_flags->log_file.size() > 0) {
366 output = fopen(G_flags->log_file.c_str(), "w");
367 } else {
368 output = stdout;
369 }
370 DecodeEventsFromFile(stdin, output);
371 } else if (G_flags->input_type == "str") {
372 ReadEventsFromFile(stdin, ReadOneStrEventFromFile);
373 } else {
374 Printf("Error: Unknown input_type value %s\n", G_flags->input_type.c_str());
375 exit(5);
376 }
377
378 ThreadSanitizerFini();
379 if (G_flags->error_exitcode && GetNumberOfFoundErrors() > 0) {
380 return G_flags->error_exitcode;
381 }
382 }
383
384 // end. {{{1
385 // vim:shiftwidth=2:softtabstop=2:expandtab:tw=80
386