// Copyright 2006 The Android Open Source Project #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <inttypes.h> #include <string.h> #include <unistd.h> #include "dmtrace.h" static const short kVersion = 2; const DmTrace::Header DmTrace::header = { 0x574f4c53, kVersion, sizeof(DmTrace::Header), 0LL }; static char *keyHeader = "*version\n" "2\n" "clock=thread-cpu\n"; static char *keyThreadHeader = "*threads\n"; static char *keyFunctionHeader = "*methods\n"; static char *keyEnd = "*end\n"; DmTrace::DmTrace() { fData = NULL; fTrace = NULL; threads = new std::vector<ThreadRecord*>; functions = new std::vector<FunctionRecord*>; } DmTrace::~DmTrace() { delete threads; delete functions; } void DmTrace::open(const char *dmtrace_file, uint64_t start_time) { fTrace = fopen(dmtrace_file, "w"); if (fTrace == NULL) { perror(dmtrace_file); exit(1); } // Make a temporary file to write the data into. char tmpData[32]; strcpy(tmpData, "/tmp/dmtrace-data-XXXXXX"); int data_fd = mkstemp(tmpData); if (data_fd < 0) { perror("Cannot create temporary file"); exit(1); } // Ensure it goes away on exit. unlink(tmpData); fData = fdopen(data_fd, "w+"); if (fData == NULL) { perror("Can't make temp data file"); exit(1); } writeHeader(fData, start_time); } void DmTrace::close() { if (fTrace == NULL) return; writeKeyFile(fTrace); // Take down how much data we wrote to the temp data file. long size = ftell(fData); // Rewind the data file and append its contents to the trace file. rewind(fData); char *data = (char *)malloc(size); fread(data, size, 1, fData); fwrite(data, size, 1, fTrace); free(data); fclose(fData); fclose(fTrace); } /* * Write values to the binary data file. */ void DmTrace::write2LE(FILE* fstream, unsigned short val) { putc(val & 0xff, fstream); putc(val >> 8, fstream); } void DmTrace::write4LE(FILE* fstream, unsigned int val) { putc(val & 0xff, fstream); putc((val >> 8) & 0xff, fstream); putc((val >> 16) & 0xff, fstream); putc((val >> 24) & 0xff, fstream); } void DmTrace::write8LE(FILE* fstream, unsigned long long val) { putc(val & 0xff, fstream); putc((val >> 8) & 0xff, fstream); putc((val >> 16) & 0xff, fstream); putc((val >> 24) & 0xff, fstream); putc((val >> 32) & 0xff, fstream); putc((val >> 40) & 0xff, fstream); putc((val >> 48) & 0xff, fstream); putc((val >> 56) & 0xff, fstream); } void DmTrace::writeHeader(FILE *fstream, uint64_t startTime) { write4LE(fstream, header.magic); write2LE(fstream, header.version); write2LE(fstream, header.offset); write8LE(fstream, startTime); } void DmTrace::writeDataRecord(FILE *fstream, int threadId, unsigned int methodVal, unsigned int elapsedTime) { write2LE(fstream, threadId); write4LE(fstream, methodVal); write4LE(fstream, elapsedTime); } void DmTrace::addFunctionEntry(int functionId, uint32_t cycle, uint32_t pid) { writeDataRecord(fData, pid, functionId, cycle); } void DmTrace::addFunctionExit(int functionId, uint32_t cycle, uint32_t pid) { writeDataRecord(fData, pid, functionId | 1, cycle); } void DmTrace::addFunction(int functionId, const char *name) { FunctionRecord *rec = new FunctionRecord; rec->id = functionId; rec->name = name; functions->push_back(rec); } void DmTrace::addFunction(int functionId, const char *clazz, const char *method, const char *sig) { // Allocate space for all the strings, plus 2 tab separators plus null byte. // We currently don't reclaim this space. int len = strlen(clazz) + strlen(method) + strlen(sig) + 3; char *name = new char[len]; sprintf(name, "%s\t%s\t%s", clazz, method, sig); addFunction(functionId, name); } void DmTrace::parseAndAddFunction(int functionId, const char *name) { // Parse the "name" string into "class", "method" and "signature". // The "name" string should look something like this: // name = "java.util.LinkedList.size()I" // and it will be parsed into this: // clazz = "java.util.LinkedList" // method = "size" // sig = "()I" // Find the first parenthesis, the start of the signature. char *paren = (char*)strchr(name, '('); // If not found, then add the original name. if (paren == NULL) { addFunction(functionId, name); return; } // Copy the signature int len = strlen(paren) + 1; char *sig = new char[len]; strcpy(sig, paren); // Zero the parenthesis so that we can search backwards from the signature *paren = 0; // Search for the last period, the start of the method name char *dot = (char*)strrchr(name, '.'); // If not found, then add the original name. if (dot == NULL || dot == name) { delete[] sig; *paren = '('; addFunction(functionId, name); return; } // Copy the method, not including the dot len = strlen(dot + 1) + 1; char *method = new char[len]; strcpy(method, dot + 1); // Zero the dot to delimit the class name *dot = 0; addFunction(functionId, name, method, sig); // Free the space we allocated. delete[] sig; delete[] method; } void DmTrace::addThread(int threadId, const char *name) { ThreadRecord *rec = new ThreadRecord; rec->id = threadId; rec->name = name; threads->push_back(rec); } void DmTrace::updateName(int threadId, const char *name) { std::vector<ThreadRecord*>::iterator iter; for (iter = threads->begin(); iter != threads->end(); ++iter) { if ((*iter)->id == threadId) { (*iter)->name = name; return; } } } void DmTrace::writeKeyFile(FILE *fstream) { fwrite(keyHeader, strlen(keyHeader), 1, fstream); writeThreads(fstream); writeFunctions(fstream); fwrite(keyEnd, strlen(keyEnd), 1, fstream); } void DmTrace::writeThreads(FILE *fstream) { std::vector<ThreadRecord*>::iterator iter; fwrite(keyThreadHeader, strlen(keyThreadHeader), 1, fstream); for (iter = threads->begin(); iter != threads->end(); ++iter) { fprintf(fstream, "%d\t%s\n", (*iter)->id, (*iter)->name); } } void DmTrace::writeFunctions(FILE *fstream) { std::vector<FunctionRecord*>::iterator iter; fwrite(keyFunctionHeader, strlen(keyFunctionHeader), 1, fstream); for (iter = functions->begin(); iter != functions->end(); ++iter) { fprintf(fstream, "0x%x\t%s\n", (*iter)->id, (*iter)->name); } }