/* Copyright (c) 2008-2010, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // This file is part of ThreadSanitizer, a dynamic data race detector. // Author: Konstantin Serebryany. // Author: Timur Iskhodzhanov. // Experimental off-line race detector. // Reads program events from a file and detects races. // See http://code.google.com/p/data-race-test // ------------- Includes ------------- {{{1 #include "thread_sanitizer.h" #include "ts_events.h" #include <stdio.h> #include <stdarg.h> #include <ctype.h> #include <time.h> // ------------- Globals ------------- {{{1 static map<string, int> *g_event_type_map; struct PcInfo { string img_name; string file_name; string rtn_name; int line; }; static map<uintptr_t, PcInfo> *g_pc_info_map; unsigned long offline_line_n; //------------- Read binary file Utils ------------ {{{1 static const int kBufSize = 65536; template<typename T> static bool Read(FILE *fp, T *res) { unsigned char buf[16]; int size = fread(buf, sizeof(T), 1, fp); *res = 0; for (unsigned int i=0; i<sizeof(T); i++) { *res <<= 8; *res += buf[i]; } return size == 1; } static bool ReadANSI(FILE *file, string *res) { char buf[kBufSize]; unsigned short length; if (!Read<unsigned short>(file, &length)) { return false; } int size = fread(buf, 1, (int)length, file); buf[length] = 0; *res = (char *)buf; return size == length; } //------------- Utils ------------------- {{{1 static EventType EventNameToEventType(const char *name) { map<string, int>::iterator it = g_event_type_map->find(name); if (it == g_event_type_map->end()) { Printf("Unknown event type: %s\n", name); } CHECK(it != g_event_type_map->end()); return (EventType)it->second; } static void InitEventTypeMap() { g_event_type_map = new map<string, int>; for (int i = 0; i < LAST_EVENT; i++) { (*g_event_type_map)[kEventNames[i]] = i; } } static void SkipCommentText(FILE *file) { char buff[kBufSize]; int i = 0; while (true) { int c = fgetc(file); if (c == EOF) break; if (c == '\n') { offline_line_n++; break; } if (i < kBufSize - 1) buff[i++] = c; } buff[i] = 0; if (buff[0] == 'P' && buff[1] == 'C') { char img[kBufSize]; char rtn[kBufSize]; char file[kBufSize]; int line = 0; unsigned long pc = 0; if (sscanf(buff, "PC %lx %s %s %s %d", (unsigned long*)&pc, img, rtn, file, &line) == 5 && pc != 0) { CHECK(g_pc_info_map); PcInfo pc_info; pc_info.img_name = img; pc_info.rtn_name = rtn; pc_info.file_name = file; pc_info.line = line; (*g_pc_info_map)[pc] = pc_info; // Printf("***** PC %lx %s\n", pc, rtn); } } if (buff[0] == '>') { // Just print the rest of comment. Printf("%s\n", buff + 2); } } static void SkipWhiteSpaceAndComments(FILE *file) { int c = 0; while (true) { c = fgetc(file); if (c == EOF) return; if (c == '#' || c == '=') { SkipCommentText(file); continue; } if (isspace(c)) continue; break; } ungetc(c, file); } typedef bool (*EventReader)(FILE *, Event *); bool ReadOneStrEventFromFile(FILE *file, Event *event) { CHECK(event); char name[1024]; uint32_t tid; unsigned long pc, a, info; SkipWhiteSpaceAndComments(file); offline_line_n++; if (5 == fscanf(file, "%s%x%lx%lx%lx", name, &tid, &pc, &a, &info)) { event->Init(EventNameToEventType(name), tid, pc, a, info); return true; } return false; } bool ProcessCodePosition(FILE *input, int *pc, string *str) { bool ok = Read<int>(input, pc); ok &= ReadANSI(input, str); return ok; } bool ProcessMessage(FILE *input, string *str) { return ReadANSI(input, str); } // Read information about event in format: [[[info] address] pc] tid. bool ProcessEvent(FILE *input, EventType type, Event *event) { bool ok = true; unsigned short tid = 0; int pc = 0; int64_t address = 0; unsigned short extra = 0; // It's tricky switch without breaks. switch (type) { case THR_START: ok &= Read<unsigned short>(input, &extra); // fallthrough. case READ: case READER_LOCK: case SIGNAL: case THR_JOIN_AFTER: case UNLOCK: case WAIT: case WRITE: case WRITER_LOCK: ok &= Read<int64_t>(input, &address); // fallthrough. case EXPECT_RACE_BEGIN: case EXPECT_RACE_END: case RTN_EXIT: case SBLOCK_ENTER: case STACK_TRACE: case THR_END: case THR_FIRST_INSN: ok &= Read<int>(input, &pc); // fallthrough. case RTN_CALL: ok &= Read<unsigned short>(input, &tid); break; default: // read unsupported EventType. Printf("Unsupported EventType %s %d\n", type, (int)type); CHECK(false); } if (type == READ || type == WRITE) { extra = 1; } event->Init(type, (int)tid, pc, address, (int)extra); return ok; } bool ReadOneBinEventFromFile(FILE *input, Event *event) { CHECK(event); bool ok = true; EventType type; unsigned char typeOrd; int pc; int line; char rtn[kBufSize]; char file[kBufSize]; string str; while (ok) { offline_line_n++; ok &= Read<unsigned char>(input, &typeOrd); if (!ok) break; type = (EventType)typeOrd; switch (type) { case PC_DESCRIPTION: ok &= ProcessCodePosition(input, &pc, &str); if (sscanf(str.c_str(), "%s %s %d", rtn, file, &line) == 3 && pc != 0) { CHECK(g_pc_info_map); PcInfo pc_info; pc_info.img_name = "java"; pc_info.rtn_name = rtn; pc_info.file_name = file; pc_info.line = line; (*g_pc_info_map)[pc] = pc_info; } break; case PRINT_MESSAGE: ok &= ProcessMessage(input, &str); // Just print the rest of comment. Printf("%s\n", str.c_str()); break; default: ok &= ProcessEvent(input, type, event); return ok; } } return false; } void DecodeEventsFromFile(FILE *input, FILE *output) { offline_line_n = 0; bool ok = true; EventType type; unsigned char typeOrd; int pc; string str; Event event; while (ok) { ok &= Read<unsigned char>(input, &typeOrd); if (!ok) break; type = (EventType)typeOrd; switch (type) { case PC_DESCRIPTION: ok &= ProcessCodePosition(input, &pc, &str); fprintf(output, "#PC %x java %s\n", pc, str.c_str()); break; case PRINT_MESSAGE: ok &= ProcessMessage(input, &str); fprintf(output, "#> %s\n", str.c_str()); break; default: ok &= ProcessEvent(input, type, &event); fprintf(output, "%s %x %x %lx %lx\n", kEventNames[event.type()], event.tid(), (unsigned int)event.pc(), (long unsigned int)event.a(), (long unsigned int)event.info()); break; } offline_line_n++; } Printf("INFO: ThreadSanitizer write %ld lines.\n", offline_line_n); } static const uint32_t max_unknown_thread = 10000; static bool known_threads[max_unknown_thread] = {}; INLINE void ReadEventsFromFile(FILE *file, EventReader event_reader_cb) { Event event; uint64_t n_events = 0; offline_line_n = 0; while (event_reader_cb(file, &event)) { //event.Print(); n_events++; uint32_t tid = event.tid(); if (event.type() == THR_START && tid < max_unknown_thread) { known_threads[tid] = true; } if (tid >= max_unknown_thread || known_threads[tid]) { ThreadSanitizerHandleOneEvent(&event); } } Printf("INFO: ThreadSanitizerOffline: %ld events read\n", n_events); } //------------- ThreadSanitizer exports ------------ {{{1 void PcToStrings(uintptr_t pc, bool demangle, string *img_name, string *rtn_name, string *file_name, int *line_no) { if (g_pc_info_map->count(pc) == 0) { *img_name = ""; *rtn_name = ""; *file_name = ""; *line_no = 0; return; } PcInfo &info = (*g_pc_info_map)[pc]; *img_name = info.img_name; *rtn_name = info.rtn_name; *file_name = info.file_name; *line_no = info.line; if (*file_name == "unknown") *file_name = ""; } string PcToRtnName(uintptr_t pc, bool demangle) { string img, rtn, file; int line; PcToStrings(pc, demangle, &img, &rtn, &file, &line); return rtn; } //------------- main ---------------------------- {{{1 int main(int argc, char *argv[]) { Printf("INFO: ThreadSanitizerOffline r%s\n", TS_VERSION); InitEventTypeMap(); g_pc_info_map = new map<uintptr_t, PcInfo>; G_flags = new FLAGS; vector<string> args(argv + 1, argv + argc); ThreadSanitizerParseFlags(&args); ThreadSanitizerInit(); CHECK(G_flags); if (G_flags->input_type == "bin") { ReadEventsFromFile(stdin, ReadOneBinEventFromFile); } else if (G_flags->input_type == "decode") { FILE* output; if (G_flags->log_file.size() > 0) { output = fopen(G_flags->log_file.c_str(), "w"); } else { output = stdout; } DecodeEventsFromFile(stdin, output); } else if (G_flags->input_type == "str") { ReadEventsFromFile(stdin, ReadOneStrEventFromFile); } else { Printf("Error: Unknown input_type value %s\n", G_flags->input_type.c_str()); exit(5); } ThreadSanitizerFini(); if (G_flags->error_exitcode && GetNumberOfFoundErrors() > 0) { return G_flags->error_exitcode; } } // end. {{{1 // vim:shiftwidth=2:softtabstop=2:expandtab:tw=80