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