The cpus, cpus.nxt, cpu macros


The cpus, cpus.nxt, & cpu macros

Three macros, provided in /usr/kvm/lib/adb on Solaris 2 systems, display some of the information in the cpu structure for each CPU on the system. The cpu structure that these macros work with is defined in /usr/include/sys/cpuvar.h , as shown in this partial view of cpuvar.h .

Example 13-4 Excerpts from /usr/include/sys/cpuvar.h
 /*   * Per-CPU data.   */  typedef struct cpu {         processorid_t   cpu_id;         /* CPU number */          volatile u_short cpu_flags;     /* flags indicating CPU state */          kthread_id_t    cpu_thread;     /* current thread */            kthread_id_t    cpu_idle_thread; /* idle thread for this CPU */            klwp_id_t       cpu_lwp;        /* current lwp (if any) */            struct cpu_callo *cpu_callo;    /* CPU callout list */            struct fpu      *cpu_fpu;       /* currently loaded fpu context */            /*             * Links - protected by cpu_lock.             */            struct cpu      *cpu_next;      /* next existing CPU */            struct cpu      *cpu_prev;            struct cpu      *cpu_next_onln; /* next online (enabled) CPU */            struct cpu      *cpu_prev_onln;            /*             * Scheduling variables.             */            disp_t          cpu_disp;       /* dispatch queue data */            char            cpu_runrun;     /* scheduling flag - set to preempt */            char            cpu_kprunrun;   /* force kernel preemption */            pri_t           cpu_chosen_level; /* priority level at which cpu */                                            /* was chosen for scheduling */            kthread_id_t    cpu_dispthread; /* thread selected for dispatch */            disp_lock_t     cpu_thread_lock; /* dispatcher lock on current thread */            /*             * Interrupt data.             */            caddr_t         cpu_intr_stack; /* interrupt stack */            int             cpu_on_intr;    /* on interrupt stack */            kthread_id_t    cpu_intr_thread; /* interrupt thread list */            u_long          cpu_intr_actv;  /* interrupt levels active (bitmask) */            int             cpu_base_spl;   /* priority for highest rupt active */            /*             * Statistics.             */            cpu_stat_t      cpu_stat;       /* per cpu statistics */            struct kstat    *cpu_kstat;     /* kstat for this cpu's statistics */            struct  kern_profiling  *cpu_profiling; /* per cpu basis */            tracedata_t     cpu_trace;      /* per cpu trace data */            /*             * Configuration information for the processor_info system call.             *             * The pi_state field of this is filled in by the processor_info             * system call code and is inaccurate at other times. The rest             * of the fields in cpu_type_info do not change after initialization.             */            processor_info_t cpu_type_info; /* config info */    } cpu_t; 

Before we look at the macros that display information held in the cpu structures, let's try to get a feel for the layout of the data. The cpu structures are double-linked. This means that each structure points to the next structure and the previous structure.

The starting point of the cpu structures is pointed to by *cpu_list .

For this diagram, we will say that there are four CPUs. To avoid a mess, we will point out the forward links only and let you draw in the backward links.

Figure 13-4. Data Layout in Four cpu Structures

graphics/13fig02.gif

Looking at the three macros that work with the cpu structures, we will see good use of the ability to write and read variables within adb . We will also see the logical negation command used. The power of adb and adb macros is well demonstrated by these three macros. Let's explore them now!

Here are the macro files:

Example 13-7 The cpus macro
 *cpu_list>c  <c>e  <c,#(#(<c))$<cpus.nxt  cpu_list/X"(cpu_list ptr is NULL)"n 
Example 13-6 The cpus.nxt macro
 .>c  *(<c+0t28)>n  <c$<<cpu  0,#(#(<n))&#(#(<n-<e))=nn  <n,#(#(<n))&#(#(<n-<e))$<cpus.nxt 
Example 13-7 The cpu macro
 ./"id"16t"flags"16t"thread"16t"idle_t"nDx16t2+2X  +/"lwp"16t"callo"16t"fpu"n3X  +/"next"16t"prev"16t"next on"16t"prev on"n4X  +$<<disp  +/"runrun"8t"kprnrn"8t"chsnlevel"8t"dispthread"16t"threadlock"nBBd8tXB3+  +/"intr_stack"16t"on_intr"16t"intr_thread"16t"intr_actv"n4X  +/"base_spl"nD472+ 

The cpus macro starts execution at cpu_list , which points to the first address of all of the cpu structures. We save the value stored at cpu_list into adb variable c. We also store it in variable e . As we'll see soon, e plays an important role in the cpus.nxt macro. The next line in cpus is the first really tricky one.

 <c,#(#(<c))$<cpus.nxt 

Starting at *cpu_list , we will call the cpus.nxt macro X number of times, where X is once again a formula: ,#(#(<c)) . However, we already know that we never return from a macro called with $< syntax, so the only values of X that really matter are zero and non-zero. If non-zero , we will call cpus.nxt only once. Now, let's figure out what X , the count, is for this command.

In adb , the # says to logically negate the value. What is a logical negation? Simply put, it's the negative of logical true and false. If a value is true (non-zero), the logical negative is false or zero. If a value is false (zero), the logical negative is true or one.

Let's apply this to the statement above from the cpus macro, using adb . Comments have been added to the side to help you follow along.

Figure 13-5 Experimenting with logical negation in adb
 Hiya...  adb -k unix.0 vmcore.0  physmem 1e05  cpu_list=X  f0170340  This is the address of cpu_list   cpu_list/X  cpu_list:  cpu_list:       f017caa0  This is what is stored at cpu_list   *cpu_list>c   We save this into adb variable "c"   <c=X  f017caa0  Here is what is in variable "c"   #(<c)=X   Here is the logical negation of it   #0=X  1  Here is the logical negation of zero   #<c=X   Just to show parentheses were optional   ##<c=X  1  And so on   ###<c=X   and so on   $q  Hiya... 

The command <c,#(#(<c))$<cpus.nxt will call the cpus.nxt macro once, if and only if the content of cpu_list is non-zero. If * cpu_list is zero, we don't call the cpus.nxt macro and, instead, continue to the command that prints out a message telling us that a null pointer was found.

Once we are in the cpus.nxt macro, we use the same ability to perform a simple "if" test; however, we also find a bitwise AND being performed.

We know variable c points to a cpu structure. Within the cpu structure, the eighth word, cpu_next , points to the next cpu structure. The ninth word, cpu_prev , points to the previous cpu structure. If there is only one CPU in the system, cpu_next and cpu_prev will both point to the one cpu structure. We set variable n to be the address of the next cpu structure.

We call the cpu macro to display lots of useful information about the cpu structure that the adb variable c is pointing to. Note that the cpu structure, as defined in cpuvar.h , contains much more information than the cpu macro actually displays. Also note, when reading /usr/include/sys/cpuvar.h , that the cpu structure has a lot of other structures built into it. This will give you headaches when you try to calculate the actual size of a cpu structure. We've been looking at Solaris 2 here. On Solaris 1 sun4m systems, the equivalent of the cpu structure ( PerCPU ) is exactly 1 megabyte in size!

When we return from the cpu macro, the current address is actually pointing to the beginning of the next cpu structure. Take another look at the cpu macro, specifically the last line, which is shown below.

 +/"base_spl"nD472+ 

After printing out the decimal value of base_spl , the cpu macro advances the current pointer by 472 positions . Why? Since the cpu structures come one after another, the author of this macro had a choice. Knowing the cpu structure size, he took this route.

Another method would be to have the cpus.nxt macro simply set the current address to the cpu_next value we found. Either method is fine; however, the method chosen in this case requires that you know exactly how big the cpu structure is, and in this case, it is very large!

Okay, now comes the fun part.... 0,#(#(<n))&#(#(<n-<e))=nn

Variable e contains the address of first cpu structure we found. Variable n now points to the next cpu structure. If e and n are the same, then we know we have walked through the loop of cpu structures and are done. In other words, if n minus e equals zero, we are done.

The & says to do a bitwise AND. For the command =nn to be executed, both sides of the & must equate to 1. If (n-e) equals zero, we know ##0 will result in zero, so =nn will not happen. The left side of the & is testing for a case where the cpu_next value was actually set to zero. While this shouldn't happen, the macro is ready for the possibility of a null pointer in cpu_next .

Do you remember what the command =nn does? It simply prints two new lines. Since we are only printing text, location 0 was safely used as the starting point for command execution.

The next command is nearly the same. The same tests are done with n and e. If there is another cpu structure to look at, we execute the cpus.nxt macro again, feeding it the pointer to the next cpu structure.



PANIC. UNIX System Crash Dump Analysis Handbook
PANIC! UNIX System Crash Dump Analysis Handbook (Bk/CD-ROM)
ISBN: 0131493868
EAN: 2147483647
Year: 1994
Pages: 289
Authors: Chris Drake

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net