Re: [hardcntr] Harware counter per process support

Dean Gaudet (
Thu, 27 Nov 1997 12:00:05 -0800 (PST)

On 27 Nov 1997, David Mentre wrote:

> c. I'll try to implement more lazy setting of counters (idea of Dean
> Gaudet), if it's possible. BTW, Dean, I have not understand your
> code. It seems that you set the counters to the "prev" task settings,
> not the "next" settings. I must admit that I'm quite confused. Could you
> explain to me your idea in private.

Sure. I figured out a cleaner way to fix the overflow bug in my post too.
I use "prev" because that's the task you're switching from before the
switch_to, and after the switch_to in the new context it's the task you've
returned to. Before switching from prev we want to read the counters and
update the total, after switching to prev we want to read the counters and
remember what they where.

And here's a better way to do it that deals with overflow. It's based
on the observation that we only need the low 32-bits because they won't
overflow in one time-slice.

struct counter_crud {
/* running total */
unsigned long long total;
/* low 32-bits of counter the last time task was switched to */
unsigned long last;

/* Suppose the counter overflows the low 32-bits, we've assumed that
* it won't completely wrap around to last again. After the "sub"
* below we know it has overflowed if carry is set. And if that's
* the case then we need to add 2^32 to the total... which we do
* with the first adc. The rest is simple 64-bit + 32-bit arith.
extern inline void update_crud_before_switch(struct counter_crud *edi,
unsigned long counter)
__asm__ __volatile__("\t"
"xor %%edx,%%edx\n\t"
"sub %%eax,[%%edi+8]\n\t"
"adc %%edx,[%%edi+4]\n\t"
"add %%eax,[%%edi]\n\t"
"adc %%edx,[%%edi+4]\n"
: /* */
: "D" (edi), "c" (counter)
: "eax", "edx");

extern inline void update_crud_after_switch(struct counter_crud *edi,
unsigned long counter)
__asm__ __volatile__("\t"
"mov %%eax,[%%edi+8]\n"
: /* */
: "D" (edi), "c" (counter)
: "eax", "edx");

> 3. Regarding the interface

I've only looked briefly at the alpha and mips counters, I'm pretty sure
they're as basic as the pentium counters. The pentium pro counters are
quite complex in relation to these. For the alpha, mips, and pentium
(and cyrix, and centaur) chips it's sufficient to have an enumerated
list of counters.

For the pentium/cyrix/centaur you've got two more bits of control:
count events at CPL3, and count events at CPL0..2.

For the pentium pro there's another 18 bits of control (which we're
interested in):

counter mask (8 bits)
invert mask (1 bit)
edge (1 bit)
unit mask (8 bits)

Full details can be found on and in the intel
documentation. But by way of an example, if you select the "instructions
retired" event and set the counter mask to 3 then you'll get a count
of the number of cycles in which 3 or more instructions were retired.
If you set invert mask then you get "3 or less". "unit mask" is used
with the L2 events, and the external bus events and allows you to snoop
other processors on the bus. "edge" allows you to count clocks or
count events. (Not all of these work as documented :)

So I'm not sure how well an abstracted interface will work out.