1 // Copyright 2006 The Android Open Source Project 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <unistd.h> 6 #include <inttypes.h> 7 #include <string.h> 8 #include <unistd.h> 9 #include "dmtrace.h" 10 11 static const short kVersion = 2; 12 13 const DmTrace::Header DmTrace::header = { 14 0x574f4c53, kVersion, sizeof(DmTrace::Header), 0LL 15 }; 16 17 static char *keyHeader = "*version\n" "2\n" "clock=thread-cpu\n"; 18 static char *keyThreadHeader = "*threads\n"; 19 static char *keyFunctionHeader = "*methods\n"; 20 static char *keyEnd = "*end\n"; 21 22 DmTrace::DmTrace() { 23 fData = NULL; 24 fTrace = NULL; 25 threads = new std::vector<ThreadRecord*>; 26 functions = new std::vector<FunctionRecord*>; 27 } 28 29 DmTrace::~DmTrace() { 30 delete threads; 31 delete functions; 32 } 33 34 void DmTrace::open(const char *dmtrace_file, uint64_t start_time) 35 { 36 fTrace = fopen(dmtrace_file, "w"); 37 if (fTrace == NULL) { 38 perror(dmtrace_file); 39 exit(1); 40 } 41 42 // Make a temporary file to write the data into. 43 char tmpData[32]; 44 strcpy(tmpData, "/tmp/dmtrace-data-XXXXXX"); 45 int data_fd = mkstemp(tmpData); 46 if (data_fd < 0) { 47 perror("Cannot create temporary file"); 48 exit(1); 49 } 50 51 // Ensure it goes away on exit. 52 unlink(tmpData); 53 fData = fdopen(data_fd, "w+"); 54 if (fData == NULL) { 55 perror("Can't make temp data file"); 56 exit(1); 57 } 58 59 writeHeader(fData, start_time); 60 } 61 62 void DmTrace::close() 63 { 64 if (fTrace == NULL) 65 return; 66 writeKeyFile(fTrace); 67 68 // Take down how much data we wrote to the temp data file. 69 long size = ftell(fData); 70 // Rewind the data file and append its contents to the trace file. 71 rewind(fData); 72 char *data = (char *)malloc(size); 73 fread(data, size, 1, fData); 74 fwrite(data, size, 1, fTrace); 75 free(data); 76 fclose(fData); 77 fclose(fTrace); 78 } 79 80 /* 81 * Write values to the binary data file. 82 */ 83 void DmTrace::write2LE(FILE* fstream, unsigned short val) 84 { 85 putc(val & 0xff, fstream); 86 putc(val >> 8, fstream); 87 } 88 89 void DmTrace::write4LE(FILE* fstream, unsigned int val) 90 { 91 putc(val & 0xff, fstream); 92 putc((val >> 8) & 0xff, fstream); 93 putc((val >> 16) & 0xff, fstream); 94 putc((val >> 24) & 0xff, fstream); 95 } 96 97 void DmTrace::write8LE(FILE* fstream, unsigned long long val) 98 { 99 putc(val & 0xff, fstream); 100 putc((val >> 8) & 0xff, fstream); 101 putc((val >> 16) & 0xff, fstream); 102 putc((val >> 24) & 0xff, fstream); 103 putc((val >> 32) & 0xff, fstream); 104 putc((val >> 40) & 0xff, fstream); 105 putc((val >> 48) & 0xff, fstream); 106 putc((val >> 56) & 0xff, fstream); 107 } 108 109 void DmTrace::writeHeader(FILE *fstream, uint64_t startTime) 110 { 111 write4LE(fstream, header.magic); 112 write2LE(fstream, header.version); 113 write2LE(fstream, header.offset); 114 write8LE(fstream, startTime); 115 } 116 117 void DmTrace::writeDataRecord(FILE *fstream, int threadId, 118 unsigned int methodVal, 119 unsigned int elapsedTime) 120 { 121 write2LE(fstream, threadId); 122 write4LE(fstream, methodVal); 123 write4LE(fstream, elapsedTime); 124 } 125 126 void DmTrace::addFunctionEntry(int functionId, uint32_t cycle, uint32_t pid) 127 { 128 writeDataRecord(fData, pid, functionId, cycle); 129 } 130 131 void DmTrace::addFunctionExit(int functionId, uint32_t cycle, uint32_t pid) 132 { 133 writeDataRecord(fData, pid, functionId | 1, cycle); 134 } 135 136 void DmTrace::addFunction(int functionId, const char *name) 137 { 138 FunctionRecord *rec = new FunctionRecord; 139 rec->id = functionId; 140 rec->name = name; 141 functions->push_back(rec); 142 } 143 144 void DmTrace::addFunction(int functionId, const char *clazz, 145 const char *method, const char *sig) 146 { 147 // Allocate space for all the strings, plus 2 tab separators plus null byte. 148 // We currently don't reclaim this space. 149 int len = strlen(clazz) + strlen(method) + strlen(sig) + 3; 150 char *name = new char[len]; 151 sprintf(name, "%s\t%s\t%s", clazz, method, sig); 152 153 addFunction(functionId, name); 154 } 155 156 void DmTrace::parseAndAddFunction(int functionId, const char *name) 157 { 158 // Parse the "name" string into "class", "method" and "signature". 159 // The "name" string should look something like this: 160 // name = "java.util.LinkedList.size()I" 161 // and it will be parsed into this: 162 // clazz = "java.util.LinkedList" 163 // method = "size" 164 // sig = "()I" 165 166 // Find the first parenthesis, the start of the signature. 167 char *paren = (char*)strchr(name, '('); 168 169 // If not found, then add the original name. 170 if (paren == NULL) { 171 addFunction(functionId, name); 172 return; 173 } 174 175 // Copy the signature 176 int len = strlen(paren) + 1; 177 char *sig = new char[len]; 178 strcpy(sig, paren); 179 180 // Zero the parenthesis so that we can search backwards from the signature 181 *paren = 0; 182 183 // Search for the last period, the start of the method name 184 char *dot = (char*)strrchr(name, '.'); 185 186 // If not found, then add the original name. 187 if (dot == NULL || dot == name) { 188 delete[] sig; 189 *paren = '('; 190 addFunction(functionId, name); 191 return; 192 } 193 194 // Copy the method, not including the dot 195 len = strlen(dot + 1) + 1; 196 char *method = new char[len]; 197 strcpy(method, dot + 1); 198 199 // Zero the dot to delimit the class name 200 *dot = 0; 201 202 addFunction(functionId, name, method, sig); 203 204 // Free the space we allocated. 205 delete[] sig; 206 delete[] method; 207 } 208 209 void DmTrace::addThread(int threadId, const char *name) 210 { 211 ThreadRecord *rec = new ThreadRecord; 212 rec->id = threadId; 213 rec->name = name; 214 threads->push_back(rec); 215 } 216 217 void DmTrace::updateName(int threadId, const char *name) 218 { 219 std::vector<ThreadRecord*>::iterator iter; 220 221 for (iter = threads->begin(); iter != threads->end(); ++iter) { 222 if ((*iter)->id == threadId) { 223 (*iter)->name = name; 224 return; 225 } 226 } 227 } 228 229 void DmTrace::writeKeyFile(FILE *fstream) 230 { 231 fwrite(keyHeader, strlen(keyHeader), 1, fstream); 232 writeThreads(fstream); 233 writeFunctions(fstream); 234 fwrite(keyEnd, strlen(keyEnd), 1, fstream); 235 } 236 237 void DmTrace::writeThreads(FILE *fstream) 238 { 239 std::vector<ThreadRecord*>::iterator iter; 240 241 fwrite(keyThreadHeader, strlen(keyThreadHeader), 1, fstream); 242 for (iter = threads->begin(); iter != threads->end(); ++iter) { 243 fprintf(fstream, "%d\t%s\n", (*iter)->id, (*iter)->name); 244 } 245 } 246 247 void DmTrace::writeFunctions(FILE *fstream) 248 { 249 std::vector<FunctionRecord*>::iterator iter; 250 251 fwrite(keyFunctionHeader, strlen(keyFunctionHeader), 1, fstream); 252 for (iter = functions->begin(); iter != functions->end(); ++iter) { 253 fprintf(fstream, "0x%x\t%s\n", (*iter)->id, (*iter)->name); 254 } 255 } 256