xref: /haiku/src/bin/debug/profile/CallgrindProfileResult.cpp (revision 9a6a20d4689307142a7ed26a1437ba47e244e73f)
1 /*
2  * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "CallgrindProfileResult.h"
8 
9 #include <errno.h>
10 #include <sys/stat.h>
11 
12 #include <algorithm>
13 #include <new>
14 #include <StackOrHeapArray.h>
15 
16 #include "Options.h"
17 #include "ProfiledEntity.h"
18 
19 
20 // #pragma mark - CallgrindImageProfileResult
21 
22 
23 CallgrindImageProfileResult::CallgrindImageProfileResult(SharedImage* image,
24 	image_id id)
25 	:
26 	ImageProfileResult(image, id),
27 	fFunctions(NULL),
28 	fOutputIndex(0)
29 {
30 }
31 
32 
33 CallgrindImageProfileResult::~CallgrindImageProfileResult()
34 {
35 	int32 symbolCount = fImage->SymbolCount();
36 	for (int32 i = 0; i < symbolCount; i++) {
37 		while (CallgrindCalledFunction* calledFunction
38 				= fFunctions[i].calledFunctions) {
39 			fFunctions[i].calledFunctions = calledFunction->next;
40 			delete calledFunction;
41 		}
42 	}
43 
44 	delete[] fFunctions;
45 }
46 
47 
48 status_t
49 CallgrindImageProfileResult::Init()
50 {
51 	int32 symbolCount = fImage->SymbolCount();
52 	fFunctions = new(std::nothrow) CallgrindFunction[symbolCount];
53 	if (fFunctions == NULL)
54 		return B_NO_MEMORY;
55 
56 	memset(fFunctions, 0, sizeof(CallgrindFunction) * symbolCount);
57 
58 	return B_OK;
59 }
60 
61 
62 void
63 CallgrindImageProfileResult::AddSymbolHit(int32 symbolIndex,
64 	CallgrindImageProfileResult* calledImage, int32 calledSymbol)
65 
66 {
67 	fTotalHits++;
68 
69 	CallgrindFunction& function = fFunctions[symbolIndex];
70 	if (calledImage != NULL) {
71 		// check whether the called function is known already
72 		CallgrindCalledFunction* calledFunction = function.calledFunctions;
73 		while (calledFunction != NULL) {
74 			if (calledFunction->image == calledImage
75 				&& calledFunction->function == calledSymbol) {
76 				break;
77 			}
78 			calledFunction = calledFunction->next;
79 		}
80 
81 		// create a new CallgrindCalledFunction object, if not known
82 		if (calledFunction == NULL) {
83 			calledFunction = new(std::nothrow) CallgrindCalledFunction(
84 				calledImage, calledSymbol);
85 			if (calledFunction == NULL)
86 				return;
87 
88 			calledFunction->next = function.calledFunctions;
89 			function.calledFunctions = calledFunction;
90 		}
91 
92 		calledFunction->hits++;
93 	} else
94 		function.hits++;
95 }
96 
97 
98 CallgrindFunction*
99 CallgrindImageProfileResult::Functions() const
100 {
101 	return fFunctions;
102 }
103 
104 
105 int32
106 CallgrindImageProfileResult::OutputIndex() const
107 {
108 	return fOutputIndex;
109 }
110 
111 
112 void
113 CallgrindImageProfileResult::SetOutputIndex(int32 index)
114 {
115 	fOutputIndex = index;
116 }
117 
118 
119 // #pragma mark - CallgrindProfileResult
120 
121 
122 CallgrindProfileResult::CallgrindProfileResult()
123 	:
124 	fTotalTicks(0),
125 	fUnkownTicks(0),
126 	fExpectedTicks(0),
127 	fDroppedTicks(0),
128 	fNextImageOutputIndex(1),
129 	fNextFunctionOutputIndex(1)
130 {
131 }
132 
133 
134 void
135 CallgrindProfileResult::AddSamples(ImageProfileResultContainer* container,
136 	addr_t* samples, int32 sampleCount)
137 {
138 	int32 unknownSamples = 0;
139 	CallgrindImageProfileResult* previousImage = NULL;
140 	int32 previousSymbol = -1;
141 
142 	// TODO: That probably doesn't work with recursive functions.
143 	for (int32 i = 0; i < sampleCount; i++) {
144 		addr_t address = samples[i];
145 		addr_t loadDelta;
146 		CallgrindImageProfileResult* image
147 			= static_cast<CallgrindImageProfileResult*>(
148 				container->FindImage(address, loadDelta));
149 		int32 symbol = -1;
150 		if (image != NULL) {
151 			symbol = image->GetImage()->FindSymbol(address - loadDelta);
152 			if (symbol >= 0) {
153 				image->AddSymbolHit(symbol, previousImage, previousSymbol);
154 				previousImage = image;
155 				previousSymbol = symbol;
156 			}
157 		} else
158 			unknownSamples++;
159 	}
160 
161 	if (unknownSamples == sampleCount)
162 		fUnkownTicks++;
163 
164 	fTotalTicks++;
165 }
166 
167 
168 void
169 CallgrindProfileResult::AddExpectedTicks(int32 expected)
170 {
171 	fExpectedTicks += expected;
172 }
173 
174 
175 void
176 CallgrindProfileResult::AddDroppedTicks(int32 dropped)
177 {
178 	fDroppedTicks += dropped;
179 }
180 
181 
182 void
183 CallgrindProfileResult::PrintResults(ImageProfileResultContainer* container)
184 {
185 	// create output file
186 
187 	// create output dir
188 	mkdir(gOptions.callgrind_directory, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
189 
190 	// get the entity name and replace slashes by hyphens
191 	char entityName[B_OS_NAME_LENGTH];
192 	strlcpy(entityName, fEntity->EntityName(), sizeof(entityName));
193 	char* slash = entityName;
194 	while ((slash = strchr(slash, '/')) != NULL)
195 		*slash = '-';
196 
197 	// create the file name
198 	char fileName[B_PATH_NAME_LENGTH];
199 	snprintf(fileName, sizeof(fileName),
200 		"%s/callgrind.out.%" B_PRId32 ".%s.%" B_PRId64 "ms",
201 		gOptions.callgrind_directory, fEntity->EntityID(), entityName,
202 		fTotalTicks * fInterval);
203 
204 	// create the file
205 	FILE* out = fopen(fileName, "w+");
206 	if (out == NULL) {
207 		fprintf(stderr, "%s: Failed to open output file \"%s\": %s\n",
208 			kCommandName, fileName, strerror(errno));
209 		return;
210 	}
211 
212 	// write the header
213 	fprintf(out, "version: 1\n");
214 	fprintf(out, "creator: Haiku profile\n");
215 	fprintf(out, "pid: %" B_PRId32 "\n", fEntity->EntityID());
216 	fprintf(out, "cmd: %s\n", fEntity->EntityName());
217 	fprintf(out, "part: 1\n\n");
218 
219 	fprintf(out, "positions: line\n");
220 	fprintf(out, "events: Ticks Time\n");
221 	fprintf(out, "summary: %" B_PRId64 " %" B_PRId64 "\n",
222 		fTotalTicks, fTotalTicks * fInterval);
223 
224 	// get hit images
225 	BStackOrHeapArray<CallgrindImageProfileResult*, 128> images(container->CountImages());
226 	int32 imageCount = GetHitImages(container, &*images);
227 
228 	for (int32 i = 0; i < imageCount; i++) {
229 		CallgrindImageProfileResult* image = images[i];
230 
231 		CallgrindFunction* functions = image->Functions();
232 		int32 imageSymbolCount = image->GetImage()->SymbolCount();
233 		for (int32 k = 0; k < imageSymbolCount; k++) {
234 			CallgrindFunction& function = functions[k];
235 			if (function.hits == 0 && function.calledFunctions == NULL)
236 				continue;
237 
238 			fprintf(out, "\n");
239 			_PrintFunction(out, image, k, false);
240 			fprintf(out, "0 %" B_PRId64 " %" B_PRId64 "\n", function.hits,
241 				function.hits * fInterval);
242 
243 			CallgrindCalledFunction* calledFunction = function.calledFunctions;
244 			while (calledFunction != NULL) {
245 				_PrintFunction(out, calledFunction->image,
246 					calledFunction->function, true);
247 				fprintf(out, "calls=%" B_PRId64 " 0\n", calledFunction->hits);
248 				fprintf(out, "0 %" B_PRId64 " %" B_PRId64 "\n",
249 					calledFunction->hits, calledFunction->hits * fInterval);
250 				calledFunction = calledFunction->next;
251 			}
252 		}
253 	}
254 
255 	// print pseudo-functions for unknown and dropped ticks
256 	if (fUnkownTicks + fDroppedTicks > 0) {
257 		fprintf(out, "\nob=<pseudo>\n");
258 
259 		if (fUnkownTicks > 0) {
260 			fprintf(out, "\nfn=unknown\n");
261 			fprintf(out, "0 %" B_PRId64 "\n", fUnkownTicks);
262 		}
263 
264 		if (fDroppedTicks > 0) {
265 			fprintf(out, "\nfn=dropped\n");
266 			fprintf(out, "0 %" B_PRId64 "\n", fDroppedTicks);
267 		}
268 	}
269 
270 	fprintf(out, "\ntotals: %" B_PRId64 " %" B_PRId64 "\n",
271 		fTotalTicks, fTotalTicks * fInterval);
272 
273 	fclose(out);
274 }
275 
276 
277 status_t
278 CallgrindProfileResult::GetImageProfileResult(SharedImage* image, image_id id,
279 	ImageProfileResult*& _imageResult)
280 {
281 	CallgrindImageProfileResult* result
282 		= new(std::nothrow) CallgrindImageProfileResult(image, id);
283 	if (result == NULL)
284 		return B_NO_MEMORY;
285 
286 	status_t error = result->Init();
287 	if (error != B_OK) {
288 		delete result;
289 		return error;
290 	}
291 
292 	_imageResult = result;
293 	return B_OK;
294 }
295 
296 
297 void
298 CallgrindProfileResult::_PrintFunction(FILE* out,
299 	CallgrindImageProfileResult* image, int32 functionIndex, bool called)
300 {
301 	if (image->OutputIndex() == 0) {
302 		// need to print the image name
303 		int32 index = fNextImageOutputIndex++;
304 		image->SetOutputIndex(index);
305 		const char* name = image->GetImage()->Name();
306 		if (name[0] == '/' || strcmp(name, "commpage") == 0) {
307 			fprintf(out,
308 				"%sob=(%" B_PRId32 ") %s\n", called ? "c" : "",
309 				index, name);
310 		} else {
311 			// add ID to image name
312 			fprintf(out,
313 				"%sob=(%" B_PRId32 ") %s:%" B_PRId32 "\n", called ? "c" : "",
314 				index, name, image->ID());
315 		}
316 	} else {
317 		// image is already known
318 		// TODO: We may not need to print it at all!
319 		fprintf(out,
320 			"%sob=(%" B_PRId32 ")\n", called ? "c" : "", image->OutputIndex());
321 	}
322 
323 	CallgrindFunction& function = image->Functions()[functionIndex];
324 	if (function.outputIndex == 0) {
325 		// need to print the function name
326 		function.outputIndex = fNextFunctionOutputIndex++;
327 		fprintf(out,
328 			"%sfn=(%" B_PRId32 ") %s\n", called ? "c" : "",
329 			function.outputIndex,
330 			image->GetImage()->Symbols()[functionIndex]->Name());
331 	} else {
332 		// function is already known
333 		fprintf(out,
334 			"%sfn=(%" B_PRId32 ")\n", called ? "c" : "", function.outputIndex);
335 	}
336 }
337