OC Systems

Home | Contact | Advanced Search 

Technology

How can I count the times a particular call chain occurs?

I have a probe that calls ap_LogTrackback in several different places. I'm trying to figure out what it all means.

I think what I'd prefer to see is the number of times that a particular call chain is exercised. What's the best way to achieve this? Are there any predefined probes or utility functions I could call, or should I try storing the call chain myself somehow in order to count what I want?

This probe, tentatively called callers.apc, will do pretty much what you require.  Edit it to name the routines you want (see example calls to AddFunction) and recompile it using this command: 

   $ apc callers.apc

Here is callers.apc:

// Demonstrate capturing the callers and merging
// the stack traces for later display.
// Note that getting the stack trace on Solaris
// is very slow due to the need to flush the
// CPU's register windows, so don't go overboard!

// *** Note that no attempt has been made to make
//     this thread-safe. 
// *** 

// Change this parameter to increase the maximum
// levels we will capture.  It's very unlikely this
// default will be too small but you might want to
// decrease it to a much smaller value to speed
// things up a little.
//
#define MAX_LEVELS 99

// We store the information in an "upside down"
// tree hierarchy. The top level tree holds a list
// of addresses, counts and sub-trees. The count is
// only stored at the "top" of a particular tree.
//
typedef struct
{
   ap_AddressT   Address;
   ap_RbTreePtrT SubTree;
   int           Count;
} AddressEntryT, *AddressEntryPtrT;

// This is the top-level tree (a red-black tree):
DECLARE_RB_TREE (AddressTree);

// This is the list of routines to which we will
// apply the probe:
typedef struct _FunctionList_
{
   ap_FunctionIdT        FunctionId;
   struct _FunctionList_ *Next;
} FunctionListT, *FunctionListPtrT;

static FunctionListPtrT FunctionList = NULL;

// Adds a function to the list and instruments it:
static void AddFunction (ap_NameT FunctionName,
                         ap_NameT Filename,
                         ap_NameT ModuleName)
{
   ap_ModuleIdT     ModuleId;
   ap_FunctionIdT   FunctionId;
   FunctionListPtrT NewEntry;
   

   if (ap_IsNoName (ModuleName))
   {
     ModuleId = ap_ApplicationModuleId ();
   }
   else
   {
     ModuleId = ap_ModuleNameToId (ModuleName);
     if (ap_IsNoModuleId (ModuleId))
     {
       ap_Error
         (ap_WarningSev,
          "Cannot find module %s "
          "for function %s\n",
          ModuleName,
          FunctionName);
       return;
     }
   }
   

   FunctionId = ap_SymbolToFunction
     (ap_SymbolNameToId (ModuleId,
                         FunctionName,
                         Filename,
                         ap_FunctionSymbol));
   if (ap_IsNoFunctionId (FunctionId))
   {
     ap_Error
       (ap_WarningSev,
        "Cannot find function %s\n",
        FunctionName);
     return;
   }
   

   // Try to instrument it:
   if (ap_InstrumentFunction (FunctionId) == 
       ap_NoProbeVectorIndex)
   {
     ap_Error
       (ap_WarningSev,
        "Cannot instrument function %s\n"
        "    Use -v on the command line"
        " for further information.");
     return;
   }
   

   // Create a new entry for the given function
   // in the list:
   NewEntry = ap_Malloc (sizeof (FunctionListT));
   NewEntry->FunctionId = FunctionId;
   NewEntry->Next       = FunctionList;
   FunctionList         = NewEntry;
} /* AddFunction */

// Compares two addresses in the list:
static int CompareAddresses
           (void *L, void *R, void *EP)
{
   AddressEntryPtrT Left;
   AddressEntryPtrT Right;
   

   Left  = (AddressEntryPtrT) L;
   Right = (AddressEntryPtrT) R;
   

   if (Left->Address > Right->Address)
   {
      return 1;
   }
   else if (Left->Address < Right->Address)
   {
      return -1;
   }
   else
   {
      return 0;
   }
} /* CompareAddresses */

// This adds a traceback to the list:
static void AddTraceback
            (ap_AddressT *Buffer,
             int         Depth)
{
   ap_RbTreePtrT    Tree = NULL;
   AddressEntryT    MatchingEntry;
   int              CurrentDepth = 0;
   AddressEntryPtrT CurrentEntry;
   

   while (CurrentDepth < Depth)
   {
     ap_RbtNodePtrT NodePtr;
     

     // Get or create the tree:
     if (Tree == NULL)
     {
       Tree = &AddressTree;
     }
     else
     {
       // Is there a sub-tree?
       if (CurrentEntry->SubTree == NULL)
       {
         // No, so create one:
         CurrentEntry->SubTree = 
           ap_Malloc (sizeof (ap_RbTreeT));
         ap_RbtInitializeTree
           (CurrentEntry->SubTree,
            ap_Malloc,
            ap_Free);
       }
       Tree = CurrentEntry->SubTree;
     }
     

     // Find this address in the tree:
     MatchingEntry.Address = Buffer [CurrentDepth];
     NodePtr = ap_RbtFind
       (Tree,
        (void *) &MatchingEntry,
        CompareAddresses,
        NULL);
     

     if (NodePtr == NULL)
     {
       // No entry so create a new one:
       CurrentEntry
         = ap_Malloc (sizeof (AddressEntryT));
       CurrentEntry->Address
         = Buffer [CurrentDepth];
       CurrentEntry->SubTree = NULL;
       CurrentEntry->Count   = 0;
       

       NodePtr = ap_RbtInsert
         (Tree,
          (void *) CurrentEntry,
          0,
          CompareAddresses,
          NULL);   
     }
     

     CurrentEntry = (AddressEntryPtrT)
       ap_RbtContents (NodePtr);
     

     // Move to the next level:
     CurrentDepth++;
   } /* while */
   

   // Increment the count for the current entry:
   CurrentEntry->Count++;

} /* AddTraceback */

// This is the format routine to print
// a traceback:
static void TracebackFormat
   (ap_AddressT *Buffer,
    int         *Depth,
    int         *Count)
{
   if (*Count > 1)
   {
     printf 
      ("   Traceback captured %d times:\n",
       *Count);
   }
   else
   {
     printf 
      ("   Traceback captured once:\n");
   }
   printf 
    ("  -----------------------------"
     "----------------------------\n");
   ap_PrintTracebackFunc (Buffer, *Depth);
   printf 
    ("  -----------------------------"
     "----------------------------\n");
   printf ("\n");
} /* TracebackFormat */

// This is the type we use for iterating through
// the tree to log the tracebacks:
typedef struct
{
   ap_AddressT Buffer [MAX_LEVELS];
   int         Depth;
} IterateDataT, *IterateDataPtrT;

// This is the iterator function we use to log
// the tracebacks. It recurses down the tree,
// storing a traceback each time it reaches a 
// non-zero count:
static ap_BooleanT DumpAddresses
                   (void *Entry, void *EP)
{
   AddressEntryPtrT AddressEntry;
   IterateDataPtrT  Data;
   

   Data         = (IterateDataPtrT) EP;
   AddressEntry = (AddressEntryPtrT) Entry;
   

   // Store the current depth
   Data->Buffer [Data->Depth]
     = AddressEntry->Address;
   

   // Do we have a count here?
   if (AddressEntry->Count != 0)
   {
     log (Data->Buffer [0 .. Data->Depth],
          Data->Depth + 1,
          AddressEntry->Count)
     with TracebackFormat;
   }
   

   // If there is a subtree, recurse into it:
   if (AddressEntry->SubTree)
   {
     Data->Depth++;
     ap_RbtIterate
       (AddressEntry->SubTree,
        DumpAddresses,
        ap_Forward,
        EP);
     Data->Depth--;
   }
   

   return TRUE;
} /* DumpAddresses */

// ************************************
// Finally, here's the probe itself:   
// ************************************

probe program
{
   probe thread
   {
     // This is the probe that gets applied
     // to each function for which we want
     // to capture traceback information:
     typedef probe
     {
       on_entry
       {
          ap_AddressT Buffer [MAX_LEVELS];
          int         Depth;
          

          // Get the traceback:
          Depth = 
            ap_GetTraceback 
               (ap_CurrentLocation,
                Buffer,
                MAX_LEVELS);
          

          // Store it:
          AddTraceback (Buffer, Depth);
       }
     } TracebackProbeT;
     

     on_entry /* to probe thread */ 
     {
       FunctionListPtrT Current;
       

       // Create a probe instance for all
       // functions we want:
       Current = FunctionList;
       while (Current)
       {
         new TracebackProbeT
             (Current->FunctionId);
         Current = Current->Next;
       }
     }
   } /* probe thread */
   

   on_entry /* to probe program */ 
   {
     ap_RbtInitializeTree 
        (&AddressTree,
         ap_Malloc,
         ap_Free);
     

     // Add the functions you want here.
     // The following examples add
     // main() and printf() in libc.so
     // respectively. Use ap_ExternSymbol
     // for the filename field (the second
     // parameter) if you're sure that
     // the function is extern, ap_NoName if
     // you're not sure and give the
     // name of the file if it's static 
     // (e.g. "myfile.c"):
     // 
     AddFunction ("main()",
                  ap_ExternSymbol,
                  NULL);
     AddFunction ("printf()",
                  ap_ExternSymbol,
                  "libc.so");
   }
   

   on_exit /* from probe program */ 
   {
     IterateDataT Data;
     

     // Log the trees out to APD file:
     Data.Depth = 0;
     ap_RbtIterate 
       (&AddressTree,
        DumpAddresses,
        ap_Forward,
        (void *) &Data);
   }
} /* probe program */

Aprobe Technology Overview

Schedule a web demo

Contact us