Delphi SampleProfiler: Как этот код звонит в ntdll.dll?

я представил часть своего приложения с помощью Профилировщика Выборки Delphi. Как большинство людей, я вижу большинство времени, проведенного внутри ntdll.dll.

Примечание: я включил опции проигнорировать Application.Idle время и вызовы от System.pas. Таким образом, это не внутри ntdll потому что приложение неактивно:

alt text

После нескольких выполнений, многократно, большинство времени, кажется, потрачено внутри ntdll.dll, но нечетная вещь состоит в том, кто вызывающая сторона:

enter image description here

Вызывающая сторона от Виртуального Treeview:

PrepareCell(PaintInfo, Window.Left, NodeBitmap.Width);    

Примечание: Приложение не внутри ntdll.dll потому что приложение неактивно, потому что вызывающая сторона не Application.Idle.

То, что смущает меня, - то, что это - эта строка сама (т.е. не что-то в PrepareCell) вызывающая сторона в ntdll. Еще более сбивающий с толку то, что:

  • не только это не что-то внутри PrepareCell()
  • это даже не установка PrepareCell (например, появляющийся переменные стека, настраивая неявные кадры исключения, и т.д.), который является вызывающей стороной. Те вещи обнаружились бы в профилировщике как горячая точка на begin в PrepareCell.

VirtualTrees.pas:

procedure TBaseVirtualTree.PrepareCell(var PaintInfo: TVTPaintInfo; WindowOrgX, MaxWidth: Integer);
begin
   ...
end;

Таким образом, я пытаюсь выяснить как эта строка:

PrepareCell(PaintInfo, Window.Left, NodeBitmap.Width);    

звонит ntdll.dll.


Единственными другими путями в являются эти три параметра:

  • PaintInfo
  • Window.Left
  • NodeBitmap.Width

Возможно, один из тех является функцией или методом считывания свойства, который звонил бы в ntdll. Таким образом, я поместил точку останова на строку и смотрю на окно CPU во времени выполнения:

alt text

Существует строка там, которая могла бы быть преступником:

call dword ptr [edx+$2c]

Но когда я следую за тем переходом, он не заканчивается в ntdll.dll, но TBitmap.GetWidth:

alt text

Который, как Вы видите, не звонит нигде; и конечно не в ntdll.dll.


Таким образом, как строка:

PrepareCell(PaintInfo, Window.Left, NodeBitmap.Width);    

вызов в ntdll.dll?


Примечание: я знаю очень хорошо, что это действительно не звонит в ntdll.dll. Таким образом, любой действительный ответ должен будет включать слова "Выборка Профилировщика, вводит в заблуждение; та строка не звонит в ntdll.dll". В ответе должно будет также быть или сказано, что большинство времени не потрачено в ntdll.dll, или что выделенная строка не является вызывающей стороной. Наконец любой ответ должен будет объяснить, почему Выборка Профилировщика является неправильной, и как это может быть зафиксировано.

Обновление 2

Что такое ntdll.dll? Ntdll является набором встроенного API Windows NT. API Win32 является оберткой вокруг ntdll.dll это похоже на Windows API, который существовал в Windows 1/2/3/9x. Для фактического вхождения в ntdll, необходимо вызвать функцию, которая использует ntdll прямо или косвенно.

Например, когда мое приложение Delphi идет неактивное, оно ожидает сообщения путем вызывания функции user32.dll:

WaitMessage;

Когда то, когда Вы на самом деле смотрите на него:

USER32.WaitMessage
  mov eax,$00001226
  mov edx,$7ffe0300
  call dword ptr [edx]
  ret

Вызывание функции, указанной в $7ffe0300 путь переходы Windows в Ring0, называя FunctionID указанным в EAX. В этом случае называемая Системная функция является 0x1226. В моей операционной системе Windows Vista, 0x1226 соответствует системной функции NtUserWaitMessage.

Это - то, как Вам входят в ntdll.dll: Вы называете его.

я отчаянно старался избегать махающего рукой неответа, когда я сформулировал исходный вопрос. Будучи очень конкретным, тщательно указывая на действительность того, что я вижу, я пытался препятствовать тому, чтобы люди игнорировали факты и попытались использовать махающий рукой аргумент.


Обновление три

я преобразовал эти два параметра:

PrepareCell(PaintInfo, Window.Left, NodeBitmap.Width);

в переменные стека:

_profiler_WindowLeft := Window.Left;
_profiler_NodeBitmapWidth := NodeBitmap.Width;
PrepareCell(PaintInfo, _profiler_WindowLeft, _profiler_NodeBitmapWidth);

Подтвердить, что узкое место не, - вызов к

  • Windows.Left, или
  • Nodebitmap. Ширина

Профилировщик все еще указывает что строка

PrepareCell(PaintInfo, _profiler_WindowLeft, _profiler_NodeBitmapWidth);

самостоятельно узкое место; ничто в PrepareCell. Это должно означать, что это - что-то в установке вызова для подготовки ячейки, или в начале PrepareCell:

VirtualTrees.pas.15746: PrepareCell(PaintInfo, _profiler_WindowLeft, _profiler_NodeBitmapWidth);
   mov eax,[ebp-$54]
   push eax
   mov edx,esi
   mov ecx,[ebp-$50]
   mov eax,[ebp-$04]
   call TBasevirtualTree.PrepareCell

Ничто в этом не звонит в ntdll. Теперь преамбула в самом PrepareCell:

VirtualTrees.pas.15746: begin
   push ebp
   mov ebp,esp
   add esp,-$44
   push ebx
   push esi
   push edi
   mov [ebp-$14],ecx
   mov [ebp-$18],edx
   mov [ebp-$1c],eax
   lea esi,[ebp-$1c]
   mov edi,[ebp-$18]

Ничто там не звонит в ntdll.dll.


Вопросы все еще остаются:

  • почему продвижение каждый является переменным на стек и двух других в регистры узкое место?
  • почему что-нибудь не в самом PrepareCell узкое место?

8
задан Glorfindel 22 July 2019 в 19:08
поделиться

2 ответа

На самом деле эта проблема была моей основной причиной, по которой я создал свой собственный профилировщик выборки:
http://code.google.com/p/asmprofiler / wiki / AsmProfilerSamplingMode

Возможно, не идеально, но вы можете попробовать. Дай мне знать, что ты думаешь об этом.

Кстати, я думаю, это связано с тем, что почти все вызовы заканчиваются вызовами ядра (запросы памяти, события рисования и т. Д.). Для вызова ядра не требуется только вычисления. Большинство вызовов заканчивается ожиданием результатов ядра:

ntdll.dll!KiFastSystemCallRet

Вы можете увидеть это в Process Explorer с представлением стека потоков, или в Delphi, или с помощью StackWalk64 API в моем " Просмотр в реальном времени "AsmProfiler:
http://code.google.com/p/asmprofiler/wiki/ProcessStackViewer

3
ответ дан 6 December 2019 в 00:06
поделиться

Вероятно, там происходят две вещи.

Первый заключается в том, что SamplingProfiler идентифицирует вызывающего, поднимаясь по стеку, пока не обнаружит то, что выглядит как действительная точка вызова в Delphi из кода Delphi.

Дело в том, что некоторые процедуры могут одновременно резервировать большой объем стека без его повторной инициализации. Это может привести к ложному срабатыванию. Единственным признаком тогда будет то, что ваше ложное срабатывание было недавно вызвано.

Вторая вещь - это локализация ntdll , о которой доподлинно известно, однако ntdll - это ваша точка ожидания в пользовательском пространстве, а, как и user197220, ntdll - это то место, где вы закончите ожидание большую часть времени вы вызываете системные вещи и ждете результата.

В вашем случае, если вы не уменьшите частоту дискретизации, вы получите 247 мс рабочего времени процессора, которое, вероятно, могло бы сойти за бездействие, если бы эти 247 выборок были собраны в течение многих секунд реального времени. Поскольку ложные срабатывания указывают на подготовку к отрисовке VirtualTree, я могу поспорить, что время ntdll на самом деле является временем отрисовки (драйвер или программное обеспечение ОС). Вы можете попробовать закомментировать код, который на самом деле рисует, чтобы быть уверенным.

1
ответ дан 6 December 2019 в 00:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: