Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Cpu usage EINZELNER PROZESSE ermitteln (https://www.delphipraxis.net/44829-cpu-usage-einzelner-prozesse-ermitteln.html)

perle 25. Apr 2005 14:22


Cpu usage EINZELNER PROZESSE ermitteln
 
ich habe glaube ich so ziemlich alle Threads hier zum Thema CPU Auslastung gelesen, allerdings war dort immer nur die Sprache von der Gesamten momentanen Auslastung. Wie kann ich aber die momentane Auslastung der CPU und die Nutzung des Arbeitsspeichers für jedes einzelne Programm einzeln ermitteln?

Ich habe gehört, dass sowas mit Hilfe von Performancecountern möglich sein soll, aber wie ??

Der Taskmanager muss das ja auch hinbekommen.

Mephistopheles 25. Apr 2005 14:54

Re: Cpu usage EINZELNER PROZESSE ermitteln
 
Hier ist die Angabe der Windowsversion von äußerster Wichtigkeit. Also, welche ist es?

perle 25. Apr 2005 14:56

Re: Cpu usage EINZELNER PROZESSE ermitteln
 
Windows 2000/XP

Falls das auch noch einen Unterschied macht, dann nur XP
-----

Mephistopheles 25. Apr 2005 15:06

Re: Cpu usage EINZELNER PROZESSE ermitteln
 
Es macht keinen ;)

Dann würde ich dich bitten, dir von der Jedi-Apilib (auf SF.net) folgende Units herunterzuladen:
\Win32API\
.\JwaNative.pas
.\JwaNtStatus.pas
.\JwaWinBase.pas
.\JwaWinNT.pas
.\JwaWinType.pas

Entweder du benutzt direkt einen CVS-Client oder die SF-Variante.

Wenn du das hast, bräuchtest du noch etwas Lektüre. Bitte melde dich einmal per PN.

Mephistopheles 25. Apr 2005 15:27

Re: Cpu usage EINZELNER PROZESSE ermitteln
 
So. Nun da du im Prinzip alles hast, werde ich mich mal kurz verabschieden um ein kleines Beipiel zu verzapfen, mit dem du dein Ziel erreichen kannst ;)

Nicht Kunst und Wissenschaft allein,
Geduld will bei dem Werke seyn.

Mephistopheles 25. Apr 2005 20:24

Re: Cpu usage EINZELNER PROZESSE ermitteln
 
Gib mir bitte bis Mittwoch Zeit. Ich muß mich jetzt erstmal anderen Dingen widmen. Ich vergesse es aber nicht. Gruß,

Luckie 25. Apr 2005 20:38

Re: Cpu usage EINZELNER PROZESSE ermitteln
 
Könntest du das dann gleich bitte in die Code-Lib einstellen? Danke.

perle 25. Apr 2005 20:51

Re: Cpu usage EINZELNER PROZESSE ermitteln
 
ja kein Problem, so drängt das nicht. Danke :)

Catbytes 26. Apr 2005 14:06

Re: Cpu usage EINZELNER PROZESSE ermitteln
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo,

aus diversen Quellen im Netz und der MSDN habe ich nun im Anhang ein kleines Beispielprojekt für dieses Problem.

Hinweis: Läuft nur unter NT-Systeme!

Kritik und Verbesserungsvorschläge sind erwünscht :???:

Mephistopheles 27. Apr 2005 12:04

Re: Cpu usage EINZELNER PROZESSE ermitteln
 
Hi,

ein gutes Beispiel. Es hat aber einen Nachteil: du brauchst ein Handle zu dem entsprechenden Prozess. Es gibt Fälle, wo du das kaum bekommen wirst ;)
Ich muß mal gucken, ob man das auch ohne Handle rausbekommt. Muß ich erst noch sehen.

@perle: Ich sitze gerade an meinem Beispiel.

perle 27. Apr 2005 12:22

Re: Cpu usage EINZELNER PROZESSE ermitteln
 
super, bin schon sehr gespannt.

@Catbytes : ich gehe mal davon aus, dass ich das Prozesshandle übergeben muss oder? Wieso bekomme ich dann teilweise negative Werte? Und warum (wenn die Auslastung bei 100% ist) verringert sich die Auslastung wieder auf 0% solange ich das Fenster hin und herschiebe?

Mephistopheles 27. Apr 2005 12:36

Re: Cpu usage EINZELNER PROZESSE ermitteln
 
Das ist der Zauber von Application.ProcessMessages() ;)

Mephistopheles 29. Apr 2005 12:30

Re: Cpu usage EINZELNER PROZESSE ermitteln
 
Liste der Anhänge anzeigen (Anzahl: 2)
Die Berechnung anhand der Werte solltest du CatBytes' Beispiel entnehmen. Ansonsten gibt es hier Code, der dir für beliebige Prozesse anhand der PID (also ohne Handle) die entsprechenden Zeiten anzeigt. Einzig die ExitTime wird von meiner Funktion nicht beachtet, da man die wirklich nur mit Handle rausbekommt (und sie für deine Berechnung auch nicht wichtig ist).

Die Units, vom JEDI-Apilib-Projekt brauchst du nach wie vor (es gibt übrigens eine aktualisierte Version von JwaWinType und JwaNative), die du dir und also runterladen solltest.

Aus der Funktion CallBackProcess() solltest du das Writeln() entfernen, sobald du es getestet hast. Weil es ja so 1. nur in Konsolenprogrammen funktioniert und 2. nur zum Testen da war.

Die wichtige Funktion für dich ist GetProcessTimesByPid()! Alle anderen brauchen dich nicht wirklich zu interessieren. Falls sie es doch tun, kann ich es noch ein wenig erläutern ;) ... Als Test gibt dieses Miniprogramm einfach aus, wann der Prozess mit der hardcodeten PID gestartet wurde. Vorzugsweise sollte die PID natürlich existieren ;)

Der Code ist PUBLIC DOMAIN, darf aber natürlich auch unter einer beliebigen OSI-zertifizierten Lizenz benutzt werden - in diesem Falle ist "Copyright (c) 2005 by Mephistopheles" anzugeben. Für die Benutzung der entsprechenden Units gelten die dort angegebenen Lizenzvereinbarungen (üblicherweise MPL für JEDI).

Kleines Projekt mit den Funktionen:
Delphi-Quellcode:
program ProcessTimesNoHandle;
{$APPTYPE CONSOLE}
uses
  Windows,
  SysUtils,
  JwaNtStatus,
  JwaWinType,
  JwaNative;

type
  TCallBackProcess = function(ps: PSYSTEM_PROCESSES; dwUserData: DWORD): BOOL; stdcall;

  PProcessTimeRecord = ^TProcessTimeRecord;
  TProcessTimeRecord = record
    PID: DWORD;
    CreationTime,
      KernelTime,
      UserTime: LARGE_INTEGER;
  end;

function ListProcesses(Callback: TCallBackProcess; dwUserData: DWORD): Boolean;
var
  Status: NTSTATUS;
  Buffer: PVOID;
  TempBuf: PSYSTEM_PROCESSES;
  BufLen: ULONG;
const
  MinQuerySize = $10000;
begin
  Result := False;
  BufLen := MinQuerySize;
  Buffer := RtlAllocateHeap(NtpGetProcessHeap(), HEAP_ZERO_MEMORY, BufLen);
  if (Assigned(Buffer)) then
  try
    Status := NtQuerySystemInformation(
      SystemProcessesAndThreadsInformation,
      Buffer,
      BufLen,
      nil);
    while (Status = STATUS_INFO_LENGTH_MISMATCH) do
    begin
      // Double the size to allocate
      BufLen := BufLen * 2;
      TempBuf := RtlReAllocateHeap(NtpGetProcessHeap(), HEAP_ZERO_MEMORY, Buffer, BufLen);
      if (not Assigned(TempBuf)) then
        Exit; // And free "Buffer" inside finally clause
      // Else assign the TempBuf to Buffer
      Buffer := TempBuf;
      // Try to query info again
      Status := NtQuerySystemInformation(
        SystemProcessesAndThreadsInformation,
        Buffer,
        BufLen,
        nil);
    end;
    // TempBuf used for pointer arithmetics
    TempBuf := Buffer;
    if (NT_SUCCESS(Status)) then
    begin
      while (True) do
      begin
        if (Assigned(Callback)) then
          if (not CallBack(TempBuf, dwUserData)) then
          // Exit loop if the callback signalled to do so.
            Break;
        // Break if there is no next entry
        if (TempBuf^.NextEntryDelta = 0) then
          Break;
        // Else go to next entry in list
        TempBuf := PSYSTEM_PROCESSES(DWORD(TempBuf) + TempBuf^.NextEntryDelta);
      end;
      Result := True;
    end;
  finally
    if (Assigned(Buffer)) then
      RtlFreeHeap(NtpGetProcessHeap(), 0, Buffer);
  end;
end;

// This MUST NOT be a local function

function CallBackProcess(ps: PSYSTEM_PROCESSES; ProcessTimeRecord: PProcessTimeRecord): BOOL; stdcall;
begin
  Result := True;
  if (Assigned(ps)) then
    if (ps^.ProcessId = ProcessTimeRecord^.PID) then
    begin
      ProcessTimeRecord^.CreationTime := ps^.CreateTime;
      ProcessTimeRecord^.KernelTime := ps^.KernelTime;
      ProcessTimeRecord^.UserTime := ps^.UserTime;
      // FIXME: This is for debugging only. Of course not needed in production code
      Writeln('PID = ', ps^.ProcessId, ' - parent = ', ps^.InheritedFromProcessId);
      // Stop going through the list
      Result := False;
    end;
end;

// Instead of only taking the times, it would be easier and more effective to
// take all information directly from the SYSTEM_PROCESS structures in the
// callback!

function GetProcessTimesByPid(
  PID: DWORD;
  var lpCreationTime: Windows.FILETIME;
  var lpKernelTime: Windows.FILETIME;
  var lpUserTime: Windows.FILETIME
  ): BOOL; stdcall;
var
  times: TProcessTimeRecord;
begin
  times.PID := PID; // PID to search for
  // We need to pass a pointer here!
  Result := ListProcesses(@CallBackProcess, DWORD(@times));
  lpCreationTime := Windows.FILETIME(times.CreationTime);
  lpKernelTime := Windows.FILETIME(times.KernelTime);
  lpUserTime := Windows.FILETIME(times.UserTime);
end;

var
  lpCreationTime,
    lpKernelTime,
    lpUserTime: Windows.FILETIME;
  cst: SYSTEMTIME;
begin
  // Hardcoded PID for testing. This should be called for each PID found
  if (GetProcessTimesByPid(2588, lpCreationTime, lpKernelTime, lpUserTime)) then
  begin
    FileTimeToSystemTime(lpCreationTime, cst);
    Writeln(Format('Process created: %.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3d', [cst.wYear, cst.wMonth, cst.wDay, cst.wHour, cst.wMinute, cst.wSecond, cst.wMilliseconds]));
  end;
  Readln;
end.
BTW: Hatte da einen dummen Fehler drin, den ich jetzt per Debugger gefunden hatte. Ursprüngliche war meine Callback-Funktion als lokale Funktion deklariert. Das ist natürlich tötlich, weil mit jedem Aufruf der Callback sich auch der Stackframe ändert. Logischerweise werden also falsche Pointerwerte vom Stack geholt und es wird versucht auf diese zu schreiben ... *plonk* -> AV! ... naja, die Lehre daraus: Niemals eine Callbackfunktion, die von einer dritten Funktion aufgerufen wird als lokale Funktion deklarieren. Hätte ich eigentlich gleich drauf kommen müssen. Übrigens: deshalb die Kopfstände mit PProcessTimeRecord usw.!

PS: Sorry, daß ich dich warten lassen habe. Bin aktuell etwas im Streß (auch außerhalb der DP ;)).
PPS: Windows.FILETIME wird explizit benutzt, damit es keinen Konflikt mit dem gleichnamigen (und gleichwertigen) Typen aus der JwaNative.pas (bzw. den zugehörigen Units) beim Kompilieren gibt.
PPPS: Im Anhang das Projekt ohne die besagten Units. Die muß man sich bitte selber hier runterladen. Welche Dateien benötigt werden steht weiter oben.

Luckie 29. Apr 2005 12:43

Re: Cpu usage EINZELNER PROZESSE ermitteln
 
Leider bekomme ich beim Kompilieren folgende Fehler auch wenn ich die Units einbinde:
[Error] ProcessTimesNoHandle.dpr(32): Undeclared identifier: 'RtlAllocateHeap'
[Error] ProcessTimesNoHandle.dpr(32): Undeclared identifier: 'NtpGetProcessHeap'
[Error] ProcessTimesNoHandle.dpr(32): Undeclared identifier: 'HEAP_ZERO_MEMORY'
[Error] ProcessTimesNoHandle.dpr(44): Undeclared identifier: 'RtlReAllocateHeap'
[Error] ProcessTimesNoHandle.dpr(76): Undeclared identifier: 'RtlFreeHeap'

Mephistopheles 29. Apr 2005 12:52

Re: Cpu usage EINZELNER PROZESSE ermitteln
 
... die sich mein Lieblings-Mod-Osterhasi aus dem CVS oder als letztes Release (ZIP oder so) runtergeladen hat?

Vertrau mir, ich weiß was ich sage, wenn ich sage: CVS-Version. Du kannst dich gern in der History von den Änderungen seit dem letzten Release (als kompaktes Paket irgendwo auf der Seite verfügbar) überzeugen (u.a. Wachstum um über 700%!). Dann verstehst du warum es nicht geht.

Mephistopheles 29. Apr 2005 13:02

Re: Cpu usage EINZELNER PROZESSE ermitteln
 
Habe an den obigen Beitrag die Units noch zusätzlich angehangen, falls es Probleme mit dem CVS gibt. Die Units wurden gestern das letzte Mal aktualisiert eingecheckt. Es ist also taufrisch und noch knusprig-warm.


Alle Zeitangaben in WEZ +1. Es ist jetzt 16:24 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz