Home | History | Annotate | Download | only in tsan
      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>
     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 
     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
     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 
     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 
    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 
    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 
    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 
    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 
    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.
    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 
    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 
    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 
    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 
    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 
    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
    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