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