xref: /haiku/src/bin/debug/profile/CallgrindProfileResult.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
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 	fDroppedTicks(0),
126 	fNextImageOutputIndex(1),
127 	fNextFunctionOutputIndex(1)
128 {
129 }
130 
131 
132 void
133 CallgrindProfileResult::AddSamples(ImageProfileResultContainer* container,
134 	addr_t* samples, int32 sampleCount)
135 {
136 	int32 unknownSamples = 0;
137 	CallgrindImageProfileResult* previousImage = NULL;
138 	int32 previousSymbol = -1;
139 
140 	// TODO: That probably doesn't work with recursive functions.
141 	for (int32 i = 0; i < sampleCount; i++) {
142 		addr_t address = samples[i];
143 		addr_t loadDelta;
144 		CallgrindImageProfileResult* image
145 			= static_cast<CallgrindImageProfileResult*>(
146 				container->FindImage(address, loadDelta));
147 		int32 symbol = -1;
148 		if (image != NULL) {
149 			symbol = image->GetImage()->FindSymbol(address - loadDelta);
150 			if (symbol >= 0) {
151 				image->AddSymbolHit(symbol, previousImage, previousSymbol);
152 				previousImage = image;
153 				previousSymbol = symbol;
154 			}
155 		} else
156 			unknownSamples++;
157 	}
158 
159 	if (unknownSamples == sampleCount)
160 		fUnkownTicks++;
161 
162 	fTotalTicks++;
163 }
164 
165 
166 void
167 CallgrindProfileResult::AddDroppedTicks(int32 dropped)
168 {
169 	fDroppedTicks += dropped;
170 }
171 
172 
173 void
174 CallgrindProfileResult::PrintResults(ImageProfileResultContainer* container)
175 {
176 	// create output file
177 
178 	// create output dir
179 	mkdir(gOptions.callgrind_directory, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
180 
181 	// get the entity name and replace slashes by hyphens
182 	char entityName[B_OS_NAME_LENGTH];
183 	strlcpy(entityName, fEntity->EntityName(), sizeof(entityName));
184 	char* slash = entityName;
185 	while ((slash = strchr(slash, '/')) != NULL)
186 		*slash = '-';
187 
188 	// create the file name
189 	char fileName[B_PATH_NAME_LENGTH];
190 	snprintf(fileName, sizeof(fileName),
191 		"%s/callgrind.out.%" B_PRId32 ".%s.%" B_PRId64 "ms",
192 		gOptions.callgrind_directory, fEntity->EntityID(), entityName,
193 		fTotalTicks * fInterval);
194 
195 	// create the file
196 	FILE* out = fopen(fileName, "w+");
197 	if (out == NULL) {
198 		fprintf(stderr, "%s: Failed to open output file \"%s\": %s\n",
199 			kCommandName, fileName, strerror(errno));
200 		return;
201 	}
202 
203 	// write the header
204 	fprintf(out, "version: 1\n");
205 	fprintf(out, "creator: Haiku profile\n");
206 	fprintf(out, "pid: %" B_PRId32 "\n", fEntity->EntityID());
207 	fprintf(out, "cmd: %s\n", fEntity->EntityName());
208 	fprintf(out, "part: 1\n\n");
209 
210 	fprintf(out, "positions: line\n");
211 	fprintf(out, "events: Ticks Time\n");
212 	fprintf(out, "summary: %" B_PRId64 " %" B_PRId64 "\n",
213 		fTotalTicks, fTotalTicks * fInterval);
214 
215 	// get hit images
216 	CallgrindImageProfileResult* images[container->CountImages()];
217 	int32 imageCount = GetHitImages(container, images);
218 
219 	for (int32 i = 0; i < imageCount; i++) {
220 		CallgrindImageProfileResult* image = images[i];
221 
222 		CallgrindFunction* functions = image->Functions();
223 		int32 imageSymbolCount = image->GetImage()->SymbolCount();
224 		for (int32 k = 0; k < imageSymbolCount; k++) {
225 			CallgrindFunction& function = functions[k];
226 			if (function.hits == 0 && function.calledFunctions == NULL)
227 				continue;
228 
229 			fprintf(out, "\n");
230 			_PrintFunction(out, image, k, false);
231 			fprintf(out, "0 %" B_PRId64 " %" B_PRId64 "\n", function.hits,
232 				function.hits * fInterval);
233 
234 			CallgrindCalledFunction* calledFunction = function.calledFunctions;
235 			while (calledFunction != NULL) {
236 				_PrintFunction(out, calledFunction->image,
237 					calledFunction->function, true);
238 				fprintf(out, "calls=%" B_PRId64 " 0\n", calledFunction->hits);
239 				fprintf(out, "0 %" B_PRId64 " %" B_PRId64 "\n",
240 					calledFunction->hits, calledFunction->hits * fInterval);
241 				calledFunction = calledFunction->next;
242 			}
243 		}
244 	}
245 
246 	// print pseudo-functions for unknown and dropped ticks
247 	if (fUnkownTicks + fDroppedTicks > 0) {
248 		fprintf(out, "\nob=<pseudo>\n");
249 
250 		if (fUnkownTicks > 0) {
251 			fprintf(out, "\nfn=unknown\n");
252 			fprintf(out, "0 %" B_PRId64 "\n", fUnkownTicks);
253 		}
254 
255 		if (fDroppedTicks > 0) {
256 			fprintf(out, "\nfn=dropped\n");
257 			fprintf(out, "0 %" B_PRId64 "\n", fDroppedTicks);
258 		}
259 	}
260 
261 	fprintf(out, "\ntotals: %" B_PRId64 " %" B_PRId64 "\n",
262 		fTotalTicks, fTotalTicks * fInterval);
263 
264 	fclose(out);
265 }
266 
267 
268 status_t
269 CallgrindProfileResult::GetImageProfileResult(SharedImage* image, image_id id,
270 	ImageProfileResult*& _imageResult)
271 {
272 	CallgrindImageProfileResult* result
273 		= new(std::nothrow) CallgrindImageProfileResult(image, id);
274 	if (result == NULL)
275 		return B_NO_MEMORY;
276 
277 	status_t error = result->Init();
278 	if (error != B_OK) {
279 		delete result;
280 		return error;
281 	}
282 
283 	_imageResult = result;
284 	return B_OK;
285 }
286 
287 
288 void
289 CallgrindProfileResult::_PrintFunction(FILE* out,
290 	CallgrindImageProfileResult* image, int32 functionIndex, bool called)
291 {
292 	if (image->OutputIndex() == 0) {
293 		// need to print the image name
294 		int32 index = fNextImageOutputIndex++;
295 		image->SetOutputIndex(index);
296 		fprintf(out,
297 			"%sob=(%" B_PRId32 ") %s:%" B_PRId32 "\n", called ? "c" : "",
298 			index, image->GetImage()->Name(), image->ID());
299 	} else {
300 		// image is already known
301 		// TODO: We may not need to print it at all!
302 		fprintf(out,
303 			"%sob=(%" B_PRId32 ")\n", called ? "c" : "", image->OutputIndex());
304 	}
305 
306 	CallgrindFunction& function = image->Functions()[functionIndex];
307 	if (function.outputIndex == 0) {
308 		// need to print the function name
309 		function.outputIndex = fNextFunctionOutputIndex++;
310 		fprintf(out,
311 			"%sfn=(%" B_PRId32 ") %s\n", called ? "c" : "",
312 			function.outputIndex,
313 			image->GetImage()->Symbols()[functionIndex]->Name());
314 	} else {
315 		// function is already known
316 		fprintf(out,
317 			"%sfn=(%" B_PRId32 ")\n", called ? "c" : "", function.outputIndex);
318 	}
319 }
320