• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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