libroot_debug: Merge guarded heap into libroot_debug.
[haiku.git] / src / system / kernel / scheduler / scheduler_profiler.cpp
blobd3399de78b2fa36fadea20d666ad3a406d4067f3
1 /*
2 * Copyright 2013, Paweł Dziepak, pdziepak@quarnos.org.
3 * Distributed under the terms of the MIT License.
4 */
6 #include "scheduler_profiler.h"
8 #include <debug.h>
9 #include <util/AutoLock.h>
11 #include <algorithm>
14 #ifdef SCHEDULER_PROFILING
17 using namespace Scheduler;
18 using namespace Scheduler::Profiling;
21 static Profiler* sProfiler;
23 static int dump_profiler(int argc, char** argv);
26 Profiler::Profiler()
28 kMaxFunctionEntries(1024),
29 kMaxFunctionStackEntries(512),
30 fFunctionData(new(std::nothrow) FunctionData[kMaxFunctionEntries]),
31 fStatus(B_OK)
33 B_INITIALIZE_SPINLOCK(&fFunctionLock);
35 if (fFunctionData == NULL) {
36 fStatus = B_NO_MEMORY;
37 return;
39 memset(fFunctionData, 0, sizeof(FunctionData) * kMaxFunctionEntries);
41 for (int32 i = 0; i < smp_get_num_cpus(); i++) {
42 fFunctionStacks[i]
43 = new(std::nothrow) FunctionEntry[kMaxFunctionStackEntries];
44 if (fFunctionStacks[i] == NULL) {
45 fStatus = B_NO_MEMORY;
46 return;
48 memset(fFunctionStacks[i], 0,
49 sizeof(FunctionEntry) * kMaxFunctionStackEntries);
51 memset(fFunctionStackPointers, 0, sizeof(int32) * smp_get_num_cpus());
55 void
56 Profiler::EnterFunction(int32 cpu, const char* functionName)
58 nanotime_t start = system_time_nsecs();
60 FunctionData* function = _FindFunction(functionName);
61 if (function == NULL)
62 return;
63 atomic_add((int32*)&function->fCalled, 1);
65 FunctionEntry* stackEntry
66 = &fFunctionStacks[cpu][fFunctionStackPointers[cpu]];
67 fFunctionStackPointers[cpu]++;
69 ASSERT(fFunctionStackPointers[cpu] < kMaxFunctionStackEntries);
71 stackEntry->fFunction = function;
72 stackEntry->fEntryTime = start;
73 stackEntry->fOthersTime = 0;
75 nanotime_t stop = system_time_nsecs();
76 stackEntry->fProfilerTime = stop - start;
80 void
81 Profiler::ExitFunction(int32 cpu, const char* functionName)
83 nanotime_t start = system_time_nsecs();
85 ASSERT(fFunctionStackPointers[cpu] > 0);
86 fFunctionStackPointers[cpu]--;
87 FunctionEntry* stackEntry
88 = &fFunctionStacks[cpu][fFunctionStackPointers[cpu]];
90 nanotime_t timeSpent = start - stackEntry->fEntryTime;
91 timeSpent -= stackEntry->fProfilerTime;
93 atomic_add64(&stackEntry->fFunction->fTimeInclusive, timeSpent);
94 atomic_add64(&stackEntry->fFunction->fTimeExclusive,
95 timeSpent - stackEntry->fOthersTime);
97 nanotime_t profilerTime = stackEntry->fProfilerTime;
98 if (fFunctionStackPointers[cpu] > 0) {
99 stackEntry = &fFunctionStacks[cpu][fFunctionStackPointers[cpu] - 1];
100 stackEntry->fOthersTime += timeSpent;
101 stackEntry->fProfilerTime += profilerTime;
103 nanotime_t stop = system_time_nsecs();
104 stackEntry->fProfilerTime += stop - start;
109 void
110 Profiler::DumpCalled(uint32 maxCount)
112 uint32 count = _FunctionCount();
114 qsort(fFunctionData, count, sizeof(FunctionData),
115 &_CompareFunctions<uint32, &FunctionData::fCalled>);
117 if (maxCount > 0)
118 count = std::min(count, maxCount);
119 _Dump(count);
123 void
124 Profiler::DumpTimeInclusive(uint32 maxCount)
126 uint32 count = _FunctionCount();
128 qsort(fFunctionData, count, sizeof(FunctionData),
129 &_CompareFunctions<nanotime_t, &FunctionData::fTimeInclusive>);
131 if (maxCount > 0)
132 count = std::min(count, maxCount);
133 _Dump(count);
137 void
138 Profiler::DumpTimeExclusive(uint32 maxCount)
140 uint32 count = _FunctionCount();
142 qsort(fFunctionData, count, sizeof(FunctionData),
143 &_CompareFunctions<nanotime_t, &FunctionData::fTimeExclusive>);
145 if (maxCount > 0)
146 count = std::min(count, maxCount);
147 _Dump(count);
151 void
152 Profiler::DumpTimeInclusivePerCall(uint32 maxCount)
154 uint32 count = _FunctionCount();
156 qsort(fFunctionData, count, sizeof(FunctionData),
157 &_CompareFunctionsPerCall<nanotime_t, &FunctionData::fTimeInclusive>);
159 if (maxCount > 0)
160 count = std::min(count, maxCount);
161 _Dump(count);
165 void
166 Profiler::DumpTimeExclusivePerCall(uint32 maxCount)
168 uint32 count = _FunctionCount();
170 qsort(fFunctionData, count, sizeof(FunctionData),
171 &_CompareFunctionsPerCall<nanotime_t, &FunctionData::fTimeExclusive>);
173 if (maxCount > 0)
174 count = std::min(count, maxCount);
175 _Dump(count);
179 /* static */ Profiler*
180 Profiler::Get()
182 return sProfiler;
186 /* static */ void
187 Profiler::Initialize()
189 sProfiler = new(std::nothrow) Profiler;
190 if (sProfiler == NULL || sProfiler->GetStatus() != B_OK)
191 panic("Scheduler::Profiling::Profiler: could not initialize profiler");
193 add_debugger_command_etc("scheduler_profiler", &dump_profiler,
194 "Show data collected by scheduler profiler",
195 "[ <field> [ <count> ] ]\n"
196 "Shows data collected by scheduler profiler\n"
197 " <field> - Field used to sort functions. Available: called,"
198 " time-inclusive, time-inclusive-per-call, time-exclusive,"
199 " time-exclusive-per-call.\n"
200 " (defaults to \"called\")\n"
201 " <count> - Maximum number of showed functions.\n", 0);
205 uint32
206 Profiler::_FunctionCount() const
208 uint32 count;
209 for (count = 0; count < kMaxFunctionEntries; count++) {
210 if (fFunctionData[count].fFunction == NULL)
211 break;
213 return count;
217 void
218 Profiler::_Dump(uint32 count)
220 kprintf("Function calls (%" B_PRId32 " functions):\n", count);
221 kprintf(" called time-inclusive per-call time-exclusive per-call "
222 "function\n");
223 for (uint32 i = 0; i < count; i++) {
224 FunctionData* function = &fFunctionData[i];
225 kprintf("%10" B_PRId32 " %14" B_PRId64 " %8" B_PRId64 " %14" B_PRId64
226 " %8" B_PRId64 " %s\n", function->fCalled,
227 function->fTimeInclusive,
228 function->fTimeInclusive / function->fCalled,
229 function->fTimeExclusive,
230 function->fTimeExclusive / function->fCalled, function->fFunction);
235 Profiler::FunctionData*
236 Profiler::_FindFunction(const char* function)
238 for (uint32 i = 0; i < kMaxFunctionEntries; i++) {
239 if (fFunctionData[i].fFunction == NULL)
240 break;
241 if (!strcmp(fFunctionData[i].fFunction, function))
242 return fFunctionData + i;
245 SpinLocker _(fFunctionLock);
246 for (uint32 i = 0; i < kMaxFunctionEntries; i++) {
247 if (fFunctionData[i].fFunction == NULL) {
248 fFunctionData[i].fFunction = function;
249 return fFunctionData + i;
251 if (!strcmp(fFunctionData[i].fFunction, function))
252 return fFunctionData + i;
255 return NULL;
259 template<typename Type, Type Profiler::FunctionData::*Member>
260 /* static */ int
261 Profiler::_CompareFunctions(const void* _a, const void* _b)
263 const FunctionData* a = static_cast<const FunctionData*>(_a);
264 const FunctionData* b = static_cast<const FunctionData*>(_b);
266 if (b->*Member > a->*Member)
267 return 1;
268 if (b->*Member < a->*Member)
269 return -1;
270 return 0;
274 template<typename Type, Type Profiler::FunctionData::*Member>
275 /* static */ int
276 Profiler::_CompareFunctionsPerCall(const void* _a, const void* _b)
278 const FunctionData* a = static_cast<const FunctionData*>(_a);
279 const FunctionData* b = static_cast<const FunctionData*>(_b);
281 Type valueA = a->*Member / a->fCalled;
282 Type valueB = b->*Member / b->fCalled;
284 if (valueB > valueA)
285 return 1;
286 if (valueB < valueA)
287 return -1;
288 return 0;
292 static int
293 dump_profiler(int argc, char** argv)
295 if (argc < 2) {
296 Profiler::Get()->DumpCalled(0);
297 return 0;
300 int32 count = 0;
301 if (argc >= 3)
302 count = parse_expression(argv[2]);
303 count = std::max(count, int32(0));
305 if (!strcmp(argv[1], "called"))
306 Profiler::Get()->DumpCalled(count);
307 else if (!strcmp(argv[1], "time-inclusive"))
308 Profiler::Get()->DumpTimeInclusive(count);
309 else if (!strcmp(argv[1], "time-inclusive-per-call"))
310 Profiler::Get()->DumpTimeInclusivePerCall(count);
311 else if (!strcmp(argv[1], "time-exclusive"))
312 Profiler::Get()->DumpTimeExclusive(count);
313 else if (!strcmp(argv[1], "time-exclusive-per-call"))
314 Profiler::Get()->DumpTimeExclusivePerCall(count);
315 else
316 print_debugger_command_usage(argv[0]);
318 return 0;
322 #endif // SCHEDULER_PROFILING