Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

FBSD: Instruktion rdtsc in Inline-Assembler

1 view
Skip to first unread message

Helmut Schellong

unread,
Jul 2, 2022, 8:00:32 AM7/2/22
to

uint64_t rdtsc(void)
{
uint32_t d, a;
__asm__ ("\n\t"
"lfence \n\t"
"rdtsc \n\t"
: [a]"=a"(a), [d]"=d"(d)
:
:
);
return (uint64_t)d<<32|(uint64_t)a;
}


Ich hatte schon immer mal vorgehabt, den Counter TSC eines Intel-X86 auszuprobieren.
Nun habe ich eine Funktion mit Inline-Assembler geschrieben, die auch funktioniert
und portabel zwischen 32bit- und 64bit-Modi ist.

Allerdings bemerkte ich, daß ich innerhalb eines Prozesses
nur _einen_ bestimmten Wert von dem Zähler erhalte.
Alle weiteren Werte sind dem im Prozeß zuerst erhaltenen Wert genau gleich.
Auch nach jeweiligen beliebig langen Verzögerungen - es wird stets
der gleiche Wert gelesen.

Wenn ich den Prozeß abbreche und neu starte, ja dann erhalte ich einen höheren Wert.
Aber natürlich erneut immer nur diesen einen Wert.

Ich weiß, daß der TSC durch ein Bit in CR4 für Anwender-Prozesse gesperrt werden kann.
Ob das Bit 1 ist, kann ich nicht feststellen, weil dabei der Prozeß wegen Bus error
mit core-dump abbricht.
Aus dem Datenbuch weiß ich, daß privilegierte Instruktionen mit herrschendem
Level 0 ausgeführt werden müssen.

Nun kenne ich zwar mehrere Level in FreeBSD:
runlevel bei shutdown, kern.securelevel, root-Privilegien, chflags, etc.
Dies sind aber alles die falschen Level-Arten.

Einzig bei dem dreistufigen Booten _könnte_ es sich um den richtigen
run-Level handeln.
Denn dabei wird von einem Level 3 bei boot1 geschrieben, und Level 0 bei BTX (TSS).

Weiß jemand etwas konkret Problemlösendes für mich, durch vertretbaren Aufwand?
Ich möchte mich wegen TSC nicht unnötig in Monate andauernde Analysearbeiten stürzen.


--
Mit freundlichen Grüßen
Helmut Schellong v...@schellong.biz
http://www.schellong.de/c.htm http://www.schellong.de/c2x.htm http://www.schellong.de/c_padding_bits.htm
http://www.schellong.de/htm/bishmnk.htm http://www.schellong.de/htm/rpar.bish.html http://www.schellong.de/htm/sieger.bish.html
http://www.schellong.de/htm/audio_proj.htm http://www.schellong.de/htm/audio_unsinn.htm http://www.schellong.de/htm/tuner.htm
http://www.schellong.de/htm/string.htm http://www.schellong.de/htm/string.c.html http://www.schellong.de/htm/deutsche_bahn.htm
http://www.schellong.de/htm/schaltungen.htm http://www.schellong.de/htm/rand.htm http://www.schellong.de/htm/dragon.c.html

Christian Weisgerber

unread,
Jul 2, 2022, 11:30:05 AM7/2/22
to
On 2022-07-02, Helmut Schellong <r...@schellong.biz> wrote:

> uint64_t rdtsc(void)
> {
> uint32_t d, a;
> __asm__ ("\n\t"
> "lfence \n\t"
> "rdtsc \n\t"
> : [a]"=a"(a), [d]"=d"(d)
> :
> :
> );
> return (uint64_t)d<<32|(uint64_t)a;
> }

> Allerdings bemerkte ich, daß ich innerhalb eines Prozesses
> nur _einen_ bestimmten Wert von dem Zähler erhalte.
> Alle weiteren Werte sind dem im Prozeß zuerst erhaltenen Wert genau gleich.

Das ist mehr eine Frage zu C. Du hast vergessen, dem Compiler zu
sagen, dass du einen flüchtigen Wert liest. Daher betrachtet der
Compiler den Wert als konstant und optimiert weitere Aufrufe der
Funktion weg.

Mit __asm__ volatile (...) funktioniert es, wie von dir erwartet.

--
Christian "naddy" Weisgerber na...@mips.inka.de

Helmut Schellong

unread,
Jul 2, 2022, 3:51:00 PM7/2/22
to
uint32_t volatile d, a;
-----------------------
Hat auch geholfen.
Deine Formulierung habe ich //notiert.

Damit hast Du mir die Augen geöffnet - Danke.
Ich kannte zwar diese Eigenschaft des clang, aber in neuem
Zusammenhang vergißt man immer mal wieder.
(Bei static vor den Funktionen generiert clang eine 'datei.s' ohne Code darin.
Er will einen Aufruf sehen...)

Ich habe das nun mit der Funktion pow() getestet
und erhielt eine Differenz von 1080 - von einem einfachen Aufruf.
Das ist das, was ich wollte.
Der TSC hat eben eine hohe Auflösung (3333 MHz).

422] a.out 11 10 3
top=0 tsc=1110
1000 3
423] a.out 11 10 3
top=0 tsc=1070
1000 3
424] a.out 11 10 3
top=0 tsc=1080
1000 3
425] a.out 11 10 3
top=0 tsc=1220
1000 3
426] a.out 11 10 3
top=0 tsc=1120
1000 3

Der Wert variiert um 10*n. Auflösung ist folglich nicht 1.
Das widerspricht zunächst dem Datenbuch...
120 Takte halte ich auch für realer als 1200.

Helmut Schellong

unread,
Jul 2, 2022, 6:38:37 PM7/2/22
to
On 07/02/2022 21:52, Frank Graf wrote:
> Am Sat, 2 Jul 2022 14:00:36 +0200 schrieb Helmut Schellong:
>
>>
>> Ich hatte schon immer mal vorgehabt, den Counter TSC eines Intel-X86
>> auszuprobieren.
[...]
>> Einzig bei dem dreistufigen Booten _könnte_ es sich um den richtigen
>> run-Level handeln.
>> Denn dabei wird von einem Level 3 bei boot1 geschrieben, und Level 0 bei
>> BTX (TSS).
>
> Ich denke mit Level 0 ist der Modus der x86 CPUs gemeint:
>
> https://de.wikipedia.org/wiki/Ring_(CPU)#Ring_0_(Kernel-Mode)
>
> Der Kernel läuft im Ring 0, die Userprozesse in Ring 3.

Ja, das paßt zu den Levels 0 und 3 beim Booten
und zum Level 0 privilegierter Instruktionen und 3 im userland.

> FreeBSD bietet eine Schnittstelle dazu (Performance counters):
>
> siehe "man pmc.tsc" bzw. "man pmc"

Danke, das kannte ich bisher nicht.

> Du willst das selber implementieren. Du könntest schauen wie das bei der
> libpmc realisiert wurde.

Wenn ich mehr als TSC will, würde ich durchaus die pmc-lib nutzen.

> So wie ich es verstehe, wird dazu das Kernelmodul "hwpmc" verwendet.
>

Ich hatte zum Ende der 1980er mal einen Geräte-Treiber unter SCO-Unix/386
implementiert, ein Dummy, dessen Code mit Level 0 gelaufen haben müßte.
Der hatte ein /dev/meins, das ich lesen und beschreiben konnte.
Und beim Systemstart wurde das Device in der Tabelle gelistet.

Unter FBSD dürfte das mit einem ladbaren Kernelmodul (kldload, kldstat, loader.conf) gehen.
Allerdings bin ich erstmal mit der simplen rdtsc-Lösung in Inline-Assembler zufrieden.

Helmut Schellong

unread,
Jul 3, 2022, 3:55:22 PM7/3/22
to
Ich habe mich inzwischen /richtig/ um die Sache gekümmert.
Resultat: Messungen ohne Schleife sind auch mit TSC nichts wert.
Bei diesen extrem kurzen Zeiträumen im Prozeß geschehen einfach
völlig widersinnige und sehr stark verfälschende Effekte.

t=rdtsc(); sleep(1); t=rdtsc()-t;
lieferte ziemlich genau 3333000000 mit geringen Sprüngen bei Wiederholung [1].
Mein Prozessor hat 3333 MHz Nennwert.
Das paßt also gut.
Alle Zahlen sind ein Vielfaches von 10.

Ich habe nun Schleifen eingerichtet mit viel volatile und 1000000 Durchläufen.
Das ergibt realistische Zeiten für die pow(), die oben 1100..1200 Takte hat.
Nämlich 336+ Takte.

[1]
446] a.out 0 1
top=0 tsc=3337607930
447] a.out 0 1
top=0 tsc=3343052710
448] a.out 0 1
top=0 tsc=3366005900
449] a.out 0 1
top=0 tsc=3350808460
450] a.out 0 1
top=0 tsc=3344029890
451] a.out 0 1
top=0 tsc=3362758630
452] a.out 0 1
top=0 tsc=3339670120
0 new messages