blob: c486c5fe9cf87de13b011c12249dbd0ed0e4ca49 [file] [log] [blame]
The Android Open Source Project5c118522008-10-21 07:00:00 -07001// Copyright 2006 The Android Open Source Project
2
3#include <stdio.h>
4#include <stdlib.h>
The Android Open Source Project95cf4642009-01-09 17:51:19 -08005#include <unistd.h>
The Android Open Source Project5c118522008-10-21 07:00:00 -07006#include <inttypes.h>
7#include <string.h>
Alexey Zaytsev62da4d92008-10-22 02:03:57 +04008#include <unistd.h>
The Android Open Source Project5c118522008-10-21 07:00:00 -07009#include "dmtrace.h"
10
11static const short kVersion = 2;
12
13const DmTrace::Header DmTrace::header = {
14 0x574f4c53, kVersion, sizeof(DmTrace::Header), 0LL
15};
16
17static char *keyHeader = "*version\n" "2\n" "clock=thread-cpu\n";
18static char *keyThreadHeader = "*threads\n";
19static char *keyFunctionHeader = "*methods\n";
20static char *keyEnd = "*end\n";
21
22DmTrace::DmTrace() {
23 fData = NULL;
24 fTrace = NULL;
25 threads = new std::vector<ThreadRecord*>;
26 functions = new std::vector<FunctionRecord*>;
27}
28
29DmTrace::~DmTrace() {
30 delete threads;
31 delete functions;
32}
33
34void 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
62void 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 */
83void DmTrace::write2LE(FILE* fstream, unsigned short val)
84{
85 putc(val & 0xff, fstream);
86 putc(val >> 8, fstream);
87}
88
89void 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
97void 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
109void 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
117void 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
126void DmTrace::addFunctionEntry(int functionId, uint32_t cycle, uint32_t pid)
127{
128 writeDataRecord(fData, pid, functionId, cycle);
129}
130
131void DmTrace::addFunctionExit(int functionId, uint32_t cycle, uint32_t pid)
132{
133 writeDataRecord(fData, pid, functionId | 1, cycle);
134}
135
136void 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
144void 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
156void 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.
Scott Tsaieda65f52009-03-21 08:17:41 +0800167 char *paren = (char*)strchr(name, '(');
The Android Open Source Project5c118522008-10-21 07:00:00 -0700168
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
Scott Tsaieda65f52009-03-21 08:17:41 +0800184 char *dot = (char*)strrchr(name, '.');
The Android Open Source Project5c118522008-10-21 07:00:00 -0700185
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
209void 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
217void 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
229void 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
237void 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
247void 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}