Ich möchte die (mehr oder weniger) genaue Menge an Anweisungen für ein Stück Code zählen. Außerdem möchte ich ein Signal erhalten, nachdem eine bestimmte Anzahl von Anweisungen übergeben wurde.
Zu diesem Zweck verwende ich das von perf_event_open .
Ich verwende die zweite Art, wie die Manpage vorschlägt, Überlaufsignale zu erhalten:
Signalüberlauf
Ereignisse können so eingestellt werden, dass bei einem Schwellenwert ein Signal ausgegeben wird ist gekreuzt. Der Signal-Handler wird über die Abfrage (2), Auswahl (2), epoll (2) und fcntl (2), Systemaufrufe.
[...]
Die andere Möglichkeit besteht in der Verwendung von PERF_EVENT_IOC_REFRESH ioctl. Dies ioctl fügt einen Zähler hinzu, der jedes Mal dekrementiert, wenn das Ereignis überläuft. Bei einem Wert ungleich Null wird beim Überlauf ein POLL_IN-Signal gesendet, nach dem Wert jedoch 0 erreicht, wird ein Signal vom Typ POLL_HUP und dem zugrunde liegenden Ereignis gesendet ist deaktiviert.
Weitere Erklärung von PERF_EVENT_IOC_REFRESH ioctl:
PERF_EVENT_IOC_REFRESH
Nicht vererbte Überlaufzähler können dies verwenden, um a zu aktivieren Zähler für eine Anzahl von Überläufen, die durch das Argument angegeben werden, Danach ist es deaktiviert. Nachfolgende Aufrufe dieses ioctl Fügen Sie den Argumentwert zur aktuellen Anzahl hinzu. Ein Signal mit POLL_IN set wird bei jedem Überlauf bis zur Zählung passieren erreicht 0; Wenn das passiert ist ein Signal mit POLL_HUP gesetzt gesendet und das Ereignis ist deaktiviert. Mit einem Argument von 0 ist als undefiniertes Verhalten betrachtet.
Ein sehr minimales Beispiel würde so aussehen:
%Vor%Im Grunde mache ich folgendes:
perf_event_open
(gibt einen Dateideskriptor zurück) fcntl
, um dem Dateideskriptor Signalübertragungsverhalten hinzuzufügen. Beim Ausführen der Payload-Schleife werden zu einem bestimmten Zeitpunkt 1000 Anweisungen ( sample_interval
) ausgeführt. Entsprechend der manage perf_event_open löst dies einen Überlauf aus, der dann einen internen Zähler dekrementiert.
Sobald dieser Zähler Null erreicht, wird ein Signal vom Typ POLL_HUP gesendet und das zugrunde liegende Ereignis wird deaktiviert.
Wenn ein Signal gesendet wird, wird der Steuerfluss des aktuellen Prozesses / Threads gestoppt und der Signal-Handler wird ausgeführt. Szenario:
Dieses Szenario würde zwei Dinge bedeuten:
ucontext
zugegriffen werden kann) würde direkt auf den Befehl verweisen, der den Überlauf verursacht hat. Grundsätzlich könnte man sagen, das Signalverhalten kann als synchron gesehen werden.
Das ist die perfekte Semantik für das, was ich erreichen möchte.
Was mich betrifft, ist das von mir konfigurierte Signal jedoch im Allgemeinen ziemlich asynchron und einige Zeit kann vergehen, bis es schließlich geliefert wird und der Signal-Handler ausgeführt wird. Dies kann für mich ein Problem darstellen.
Betrachten Sie zum Beispiel das folgende Szenario:
Dieses Szenario würde zwei Dinge bedeuten:
Bisher habe ich oben ein Beispiel getestet und habe verpasste Anweisungen, die das erste Szenario unterstützen würden.
Allerdings würde ich gerne wissen, ob ich mich auf diese Annahme verlassen kann oder nicht. Was passiert im Kernel?
Ich möchte die (mehr oder weniger) genaue Menge an Anweisungen für ein Stück Code zählen. Außerdem möchte ich ein Signal erhalten, nachdem eine bestimmte Anzahl von Anweisungen übergeben wurde.
Sie haben zwei Aufgaben, die miteinander in Konflikt stehen können. Wenn Sie eine Zählung erhalten möchten (genaue Mengen eines Hardware-Ereignisses), verwenden Sie einfach die Leistungsüberwachungseinheit Ihrer CPU im Zählmodus (setzen Sie nicht sample_period
/ sample_freq
von perf_event_attr
Struktur verwendet) und platzieren Sie den Messcode in Ihrem Zielprogramm (wie in Ihrem Beispiel). In diesem Modus werden gemäß der man-Seite von perf_event_open
keine Überläufe generiert ( Die PMU der CPU sind normalerweise 64 Bit breit und überlaufen nicht, wenn sie nicht auf einen kleinen negativen Wert eingestellt sind, wenn der Abtastmodus verwendet wird:
Überläufe werden nur durch Stichprobenereignisse generiert (sample_period muss einen Wert ungleich Null haben).
Um einen Teil des Programms zu zählen, geben Sie ioctl
s von perf_event_open fd ein, wie in man beschrieben Seite
perf_event ioctl Aufrufe - Verschiedene ioctls wirken auf perf_event_open () Dateideskriptoren: PERF_EVENT_IOC_ENABLE ... PERF_EVENT_IOC_DISABLE ... PERF_EVENT_IOC_RESET
Sie können den aktuellen Wert mit rdpmc
(auf x86) oder mit read
syscall auf dem fd lesen, wie im kurzen Beispiel von die man-Seite :
Zusätzlich möchte ich ein Signal erhalten, nachdem eine bestimmte Anzahl von Anweisungen übergeben wurde.
Wollen Sie wirklich Signal bekommen oder brauchen Sie nur Anweisungszeiger bei 1000 ausgeführten Befehlen? Wenn Sie Zeiger sammeln möchten, verwenden Sie perf_even_open
mit dem Sampling-Modus, aber tun Sie es mit einem anderen Programm , um die Messung des Ereignissammlungscodes zu deaktivieren. Außerdem wird es weniger negative Auswirkungen auf Ihr Zielprogramm haben, wenn Sie nicht für jeden Überlauf Signale verwenden (mit einer großen Menge an Kernel-Tracer-Interaktionen und Wechsel vom / zum Kernel), sondern stattdessen Funktionen von perf_events verwenden, um mehrere Überlaufereignisse zu sammeln in einzelnen mmap-Puffer und Abfrage auf diesem Puffer. Bei Überlauf-Interrupt von PMU wird der Interrupt-Interrupt-Handler aufgerufen, um den Instruktionszeiger in den Puffer zu speichern, und dann wird das Zählen zurückgesetzt und das Programm kehrt zur Ausführung zurück. In Ihrem Beispiel wird der Interrupt-Interrupt-Handler Ihr Programm wecken, mehrere Syscalls ausführen, zum Kernel zurückkehren und dann wird der Kernel den Zielcode neu starten (also ist der Overhead pro Sample größer als bei Verwendung von mmap und Parsing). Mit precise_ip
flag können Sie erweiterte Sampling Ihrer PMU aktivieren (wenn es einen solchen Modus hat, wie PEBS und PREC_DIST in Intel x86 / em64t für einige Zähler wie INST_RETIRED, UOPS_RETIRED, BR_INST_RETIRED, BR_MISP_RETIRED, MEM_UOPS_RETIRED, MEM_LOAD_UOPS_RETIRED, MEM_LOAD_UOPS_LLC_HIT_RETIRED und mit einfachem Hack bis cycles
too; oder wie IBS von AMD x86 / amd64; Papier über PEBS und IBS ) , wenn die Befehlsadresse direkt von der Hardware mit niedrigem Schlupf gespeichert wird. Einige sehr fortschrittliche PMUs können Sampling in Hardware durchführen und Überlaufinformationen mehrerer Ereignisse in Reihe mit automatischem Reset des Zählers ohne Softwareunterbrechungen speichern (einige Beschreibungen auf precise_ip
sind in derselben Zeitung ).
Ich weiß nicht, ob es im perf_events-Subsystem und in Ihrer CPU möglich ist, zwei per_event-Tasks gleichzeitig aktiv zu haben: Beide zählen Ereignisse im Zielprozess und gleichzeitig haben sie Samplings von anderen Prozessen. Mit Advanced PMU kann dies in der Hardware möglich sein und perf_events im modernen Kernel erlauben es. Aber Sie geben keine Details zu Ihrer Kernel-Version und Ihrem CPU-Hersteller und Ihrer CPU an, daher können wir diesen Teil nicht beantworten.
Sie können auch versuchen, andere APIs auf PMU wie PAPI oder likwid ( Ссылка ) zuzugreifen. Einige von ihnen lesen möglicherweise direkt PMU-Register (manchmal MSR) und können die Abtastung zur gleichen Zeit ermöglichen, wenn das Zählen aktiviert ist.