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), "%s/callgrind.out.%ld.%s.%lldms",
191		gOptions.callgrind_directory, fEntity->EntityID(), entityName,
192		fTotalTicks * fInterval);
193
194	// create the file
195	FILE* out = fopen(fileName, "w+");
196	if (out == NULL) {
197		fprintf(stderr, "%s: Failed to open output file \"%s\": %s\n",
198			kCommandName, fileName, strerror(errno));
199		return;
200	}
201
202	// write the header
203	fprintf(out, "version: 1\n");
204	fprintf(out, "creator: Haiku profile\n");
205	fprintf(out, "pid: %ld\n", fEntity->EntityID());
206	fprintf(out, "cmd: %s\n", fEntity->EntityName());
207	fprintf(out, "part: 1\n\n");
208
209	fprintf(out, "positions: line\n");
210	fprintf(out, "events: Ticks Time\n");
211	fprintf(out, "summary: %lld %lld\n", fTotalTicks, fTotalTicks * fInterval);
212
213	// get hit images
214	CallgrindImageProfileResult* images[container->CountImages()];
215	int32 imageCount = GetHitImages(container, images);
216
217	for (int32 i = 0; i < imageCount; i++) {
218		CallgrindImageProfileResult* image = images[i];
219
220		CallgrindFunction* functions = image->Functions();
221		int32 imageSymbolCount = image->GetImage()->SymbolCount();
222		for (int32 k = 0; k < imageSymbolCount; k++) {
223			CallgrindFunction& function = functions[k];
224			if (function.hits == 0 && function.calledFunctions == NULL)
225				continue;
226
227			fprintf(out, "\n");
228			_PrintFunction(out, image, k, false);
229			fprintf(out, "0 %lld %lld\n", function.hits,
230				function.hits * fInterval);
231
232			CallgrindCalledFunction* calledFunction = function.calledFunctions;
233			while (calledFunction != NULL) {
234				_PrintFunction(out, calledFunction->image,
235					calledFunction->function, true);
236				fprintf(out, "calls=%lld 0\n", calledFunction->hits);
237				fprintf(out, "0 %lld %lld\n", calledFunction->hits,
238					calledFunction->hits * fInterval);
239				calledFunction = calledFunction->next;
240			}
241		}
242	}
243
244	// print pseudo-functions for unknown and dropped ticks
245	if (fUnkownTicks + fDroppedTicks > 0) {
246		fprintf(out, "\nob=<pseudo>\n");
247
248		if (fUnkownTicks > 0) {
249			fprintf(out, "\nfn=unknown\n");
250			fprintf(out, "0 %lld\n", fUnkownTicks);
251		}
252
253		if (fDroppedTicks > 0) {
254			fprintf(out, "\nfn=dropped\n");
255			fprintf(out, "0 %lld\n", fDroppedTicks);
256		}
257	}
258
259	fprintf(out, "\ntotals: %lld %lld\n", fTotalTicks, fTotalTicks * fInterval);
260
261	fclose(out);
262}
263
264
265status_t
266CallgrindProfileResult::GetImageProfileResult(SharedImage* image, image_id id,
267	ImageProfileResult*& _imageResult)
268{
269	CallgrindImageProfileResult* result
270		= new(std::nothrow) CallgrindImageProfileResult(image, id);
271	if (result == NULL)
272		return B_NO_MEMORY;
273
274	status_t error = result->Init();
275	if (error != B_OK) {
276		delete result;
277		return error;
278	}
279
280	_imageResult = result;
281	return B_OK;
282}
283
284
285void
286CallgrindProfileResult::_PrintFunction(FILE* out,
287	CallgrindImageProfileResult* image, int32 functionIndex, bool called)
288{
289	if (image->OutputIndex() == 0) {
290		// need to print the image name
291		int32 index = fNextImageOutputIndex++;
292		image->SetOutputIndex(index);
293		fprintf(out, "%sob=(%ld) %s:%ld\n", called ? "c" : "", index,
294			image->GetImage()->Name(), image->ID());
295	} else {
296		// image is already known
297		// TODO: We may not need to print it at all!
298		fprintf(out, "%sob=(%ld)\n", called ? "c" : "", image->OutputIndex());
299	}
300
301	CallgrindFunction& function = image->Functions()[functionIndex];
302	if (function.outputIndex == 0) {
303		// need to print the function name
304		function.outputIndex = fNextFunctionOutputIndex++;
305		fprintf(out, "%sfn=(%ld) %s\n", called ? "c" : "", function.outputIndex,
306			image->GetImage()->Symbols()[functionIndex]->Name());
307	} else {
308		// function is already known
309		fprintf(out, "%sfn=(%ld)\n", called ? "c" : "", function.outputIndex);
310	}
311}
312