2 * Copyright 2013, Paweł Dziepak, pdziepak@quarnos.org.
3 * Distributed under the terms of the MIT License.
6 #include "scheduler_profiler.h"
9 #include <util/AutoLock.h>
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
);
28 kMaxFunctionEntries(1024),
29 kMaxFunctionStackEntries(512),
30 fFunctionData(new(std::nothrow
) FunctionData
[kMaxFunctionEntries
]),
33 B_INITIALIZE_SPINLOCK(&fFunctionLock
);
35 if (fFunctionData
== NULL
) {
36 fStatus
= B_NO_MEMORY
;
39 memset(fFunctionData
, 0, sizeof(FunctionData
) * kMaxFunctionEntries
);
41 for (int32 i
= 0; i
< smp_get_num_cpus(); i
++) {
43 = new(std::nothrow
) FunctionEntry
[kMaxFunctionStackEntries
];
44 if (fFunctionStacks
[i
] == NULL
) {
45 fStatus
= B_NO_MEMORY
;
48 memset(fFunctionStacks
[i
], 0,
49 sizeof(FunctionEntry
) * kMaxFunctionStackEntries
);
51 memset(fFunctionStackPointers
, 0, sizeof(int32
) * smp_get_num_cpus());
56 Profiler::EnterFunction(int32 cpu
, const char* functionName
)
58 nanotime_t start
= system_time_nsecs();
60 FunctionData
* function
= _FindFunction(functionName
);
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
;
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
;
110 Profiler::DumpCalled(uint32 maxCount
)
112 uint32 count
= _FunctionCount();
114 qsort(fFunctionData
, count
, sizeof(FunctionData
),
115 &_CompareFunctions
<uint32
, &FunctionData::fCalled
>);
118 count
= std::min(count
, maxCount
);
124 Profiler::DumpTimeInclusive(uint32 maxCount
)
126 uint32 count
= _FunctionCount();
128 qsort(fFunctionData
, count
, sizeof(FunctionData
),
129 &_CompareFunctions
<nanotime_t
, &FunctionData::fTimeInclusive
>);
132 count
= std::min(count
, maxCount
);
138 Profiler::DumpTimeExclusive(uint32 maxCount
)
140 uint32 count
= _FunctionCount();
142 qsort(fFunctionData
, count
, sizeof(FunctionData
),
143 &_CompareFunctions
<nanotime_t
, &FunctionData::fTimeExclusive
>);
146 count
= std::min(count
, maxCount
);
152 Profiler::DumpTimeInclusivePerCall(uint32 maxCount
)
154 uint32 count
= _FunctionCount();
156 qsort(fFunctionData
, count
, sizeof(FunctionData
),
157 &_CompareFunctionsPerCall
<nanotime_t
, &FunctionData::fTimeInclusive
>);
160 count
= std::min(count
, maxCount
);
166 Profiler::DumpTimeExclusivePerCall(uint32 maxCount
)
168 uint32 count
= _FunctionCount();
170 qsort(fFunctionData
, count
, sizeof(FunctionData
),
171 &_CompareFunctionsPerCall
<nanotime_t
, &FunctionData::fTimeExclusive
>);
174 count
= std::min(count
, maxCount
);
179 /* static */ Profiler
*
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);
206 Profiler::_FunctionCount() const
209 for (count
= 0; count
< kMaxFunctionEntries
; count
++) {
210 if (fFunctionData
[count
].fFunction
== NULL
)
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 "
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
)
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
;
259 template<typename Type
, Type
Profiler::FunctionData::*Member
>
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
)
268 if (b
->*Member
< a
->*Member
)
274 template<typename Type
, Type
Profiler::FunctionData::*Member
>
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
;
293 dump_profiler(int argc
, char** argv
)
296 Profiler::Get()->DumpCalled(0);
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
);
316 print_debugger_command_usage(argv
[0]);
322 #endif // SCHEDULER_PROFILING