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
45static map<string, int> *g_event_type_map;
46struct PcInfo {
47  string img_name;
48  string file_name;
49  string rtn_name;
50  int line;
51};
52
53static map<uintptr_t, PcInfo> *g_pc_info_map;
54
55unsigned long offline_line_n;
56//------------- Read binary file Utils ------------ {{{1
57static const int kBufSize = 65536;
58
59template<typename T>
60static 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
72static 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
84static 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
93static 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
100static 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
139static 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
154typedef bool (*EventReader)(FILE *, Event *);
155
156bool 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
170bool ProcessCodePosition(FILE *input, int *pc, string *str) {
171  bool ok = Read<int>(input, pc);
172  ok &= ReadANSI(input, str);
173  return ok;
174}
175
176bool ProcessMessage(FILE *input, string *str) {
177  return ReadANSI(input, str);
178}
179
180// Read information about event in format: [[[info] address] pc] tid.
181bool 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
226bool 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
267void 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
300static const uint32_t max_unknown_thread = 10000;
301
302static bool known_threads[max_unknown_thread] = {};
303
304INLINE 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
323void 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
342string 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
349int 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