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
22CallgrindImageProfileResult::CallgrindImageProfileResult(SharedImage* image,
23	image_id id)
24	:
25	ImageProfileResult(image, id),
26	fFunctions(NULL),
27	fOutputIndex(0)
28{
29}
30
31
32CallgrindImageProfileResult::~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
47status_t
48CallgrindImageProfileResult::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
61void
62CallgrindImageProfileResult::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
97CallgrindFunction*
98CallgrindImageProfileResult::Functions() const
99{
100	return fFunctions;
101}
102
103
104int32
105CallgrindImageProfileResult::OutputIndex() const
106{
107	return fOutputIndex;
108}
109
110
111void
112CallgrindImageProfileResult::SetOutputIndex(int32 index)
113{
114	fOutputIndex = index;
115}
116
117
118// #pragma mark - CallgrindProfileResult
119
120
121CallgrindProfileResult::CallgrindProfileResult()
122	:
123	fTotalTicks(0),
124	fUnkownTicks(0),
125	fDroppedTicks(0),
126	fNextImageOutputIndex(1),
127	fNextFunctionOutputIndex(1)
128{
129}
130
131
132void
133CallgrindProfileResult::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
166void
167CallgrindProfileResult::AddDroppedTicks(int32 dropped)
168{
169	fDroppedTicks += dropped;
170}
171
172
173void
174CallgrindProfileResult::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
268status_t
269CallgrindProfileResult::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
288void
289CallgrindProfileResult::_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