Что я сделал, когда не могу использовать свою любимую технику , так это. Это неуклюже и дает информацию с низким разрешением, но это работает. Во-первых, есть глобальный стек строк. Это в C, но вы можете адаптировать его к C #:
int nStack = 0;
char* stack[10000];
Затем при входе и выходе из каждой подпрограммы, для которой у вас есть исходный код, нажмите / вытолкните название подпрограммы:
void EveryFunction(){
int iStack = nStack++; stack[iStack] = "EveryFunction";
... code inside function
nStack = iStack; stack[iStack] = NULL;
}
Так что теперь стек [0..nStack] поддерживает текущий стек вызовов (за вычетом номеров строк, из которых вызываются функции), поэтому он не так хорош, как реальный стек вызовов, но лучше, чем ничего.
Теперь вам нужен способ сделать его снимки в случайное или псевдослучайное время. Иметь еще одну глобальную переменную и процедуру для просмотра:
time_t timeToSnap;
void CheckForSnap(){
time_t now = time(NULL);
if (now >= timeToSnap){
if (now - timeToSnap > 10000) timeToSnap = now; // don't take snaps since 1970
timeToSnap += 1; // setup time for next snapshot
// print stack to snapshot file
}
}
Теперь добавьте вызовы к CheckForSnap
по всему коду, особенно в низкоуровневых подпрограммах. Когда прогон закончен, у вас есть файл сэмплов стека. Вы можете посмотреть на них за неожиданное поведение. Например, любая функция, отображаемая на значительной части выборок, имеет время включения, примерно равное этой доле.
Как я уже сказал, это лучше, чем ничего. У него есть недостатки:
- Он не захватывает номера строк, откуда поступают вызовы, поэтому, если вы обнаружите функцию с подозрительно большим временем, вам нужно порыться в ней, чтобы найти трудоемкий код.
- Он добавляет значительные накладные расходы, а именно все вызовы к
time(NULL)
, поэтому, когда вы удалите все свои большие проблемы, будет труднее найти маленькие.
- Если ваша программа тратит значительное время на ожидание ввода-вывода или пользовательского ввода, вы увидите кучу сэмплов, накопленных после этого ввода-вывода. Если это файловый ввод-вывод, это полезная информация, но если это пользовательский ввод, вам придется отказаться от этих семплов, потому что все, что они говорят, это то, что вы тратите время.
Важно понимать несколько вещей:
- Вопреки общепринятому мнению, точность измерения времени (и, следовательно, большого количества образцов) не важна. Важно то, что выборки происходят в то время, когда вы ожидаете, пока программа выполнит свою работу.
- Также вопреки принятой мудрости, вы не ищете граф вызовов, вам не нужно заботиться о рекурсии, вам не нужно заботиться о том, сколько миллисекунд занимает любая подпрограмма или сколько раз она вызывается, и вам не нужно заботиться о разнице между включенным и исключительным временем или о разнице между процессором и временем настенных часов. нужно заботиться о том, какой процент времени он находится в стеке, потому что это количество времени, за которое он отвечает, в том смысле, что если бы вы могли как-то это сделать рутина не занимает много времени, поэтому ваше общее время уменьшится.