pcp
[Top] [All Lists]

Re: Add dtrace support to pmcd

To: Stan Cox <scox@xxxxxxxxxx>
Subject: Re: Add dtrace support to pmcd
From: fche@xxxxxxxxxx (Frank Ch. Eigler)
Date: Fri, 23 Aug 2013 13:37:57 -0400
Cc: Nathan Scott <nathans@xxxxxxxxxx>, pcp@xxxxxxxxxxx
Delivered-to: pcp@xxxxxxxxxxx
In-reply-to: <52177ABC.6050809@xxxxxxxxxx> (Stan Cox's message of "Fri, 23 Aug 2013 11:07:40 -0400")
References: <52126937.6090500@xxxxxxxxxx> <696967854.1385419.1376995484880.JavaMail.root@xxxxxxxxxx> <1212383964.2122435.1377038280215.JavaMail.root@xxxxxxxxxx> <52177ABC.6050809@xxxxxxxxxx>
User-agent: Gnus/5.1008 (Gnus v5.10.8) Emacs/21.4 (gnu/linux)
Hi, Stan -

scox wrote:

> [...]

v2 looks pretty good.  Some suggestions:

> [...]
> --- a/src/libpcp_pmcd/src/trace.c
> +++ b/src/libpcp_pmcd/src/trace.c
> @@ -17,6 +17,12 @@
>   */
>  
>  #include "pmcd.h"
> +#include "config.h"
> +#if ENABLE_DTRACE
> +#include "probes.h"
> +#else
> +#include "probes_nodtrace.h"
> +#endif

Instead of a separate probes_nodtrace.h file, how about just
adding the

#define PROBE_PMCD(a,b,c,d)

inline instead?


> [...]
> diff --git a/dev/null b/src/pmcd/src/pmcd.stp
> --- /dev/null 2013-07-18 08:12:04.174028131 -0400
> +++ ../pmcd.stp       2013-08-21 16:21:37.179388001 -0400

It's not clear what this script wants to be.  If a single-purpose
end-user script, no problem.  If something for the
/usr/share/systemtap/tapset, then it needs to be split into reusable
library bits and a sample 'probe pmcd.* { trace stuff }' script.


> @@ -0,0 +1,214 @@
> +%{
> +  /* From impl.h */
> +typedef struct {
> +#ifdef HAVE_BITFIELDS_LTOR
> +     unsigned int    flag : 1;
> +     unsigned int    domain : 9;
> +     unsigned int    cluster : 12;
> +     unsigned int    item : 10;
> +#else
> +     unsigned int    item : 10;
> +     unsigned int    cluster : 12;
> +     unsigned int    domain : 9;
> +     unsigned int    flag : 1;
> +#endif
> +
> +} __pmID_int;
> +
> +typedef struct {
> +#ifdef HAVE_BITFIELDS_LTOR
> +     int             flag : 1;
> +     unsigned int    domain : 9;
> +     unsigned int    serial : 22;
> +#else
> +     unsigned int    serial : 22;
> +     unsigned int    domain : 9;
> +     int             flag : 1;
> +#endif
> +} __pmInDom_int;
> +
> +%}
> +
> +
> +# function _pcp_indom_domain:long (sp_offset:long) %{ /* pure */
> +#  __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
> +#         STAP_RETVALUE = ((long) CONTEXT->kregs) + STAP_ARG_sp_offset;
> +# %}
> +
> +function pmid_item:long (id:long) %{
> +  STAP_RETVALUE = ((__pmID_int*)(&STAP_ARG_id))->item;
> +%}
> +
> +function pmid_cluster:long (id:long) %{
> +  STAP_RETVALUE = ((__pmID_int*)(&STAP_ARG_id))->cluster;
> +%}
> +
> +function pmid_domain:long (id:long) %{
> +  STAP_RETVALUE = ((__pmID_int*)(&STAP_ARG_id))->domain;
> +%}
> +
> +function pmInDom_domain:long (id:long) %{
> +  STAP_RETVALUE = ((__pmInDom_int*)(&STAP_ARG_id))->domain;
> +%}
> +
> +function pmInDom_serial:long (id:long) %{
> +  STAP_RETVALUE = ((__pmInDom_int*)(&STAP_ARG_id))->serial;
> +%}

(FWIW, instead of inline-c code, could use script level binary
rotate/mask operators here, perhaps with a twist of %( arch = ... %)
conditionalizing for the LTOR vs RTOL kind.


> +function pdu_type:string (type:long) 
> +{
> +    if (type == PDU_ERROR) res = "ERROR";
> +    else if (type == PDU_RESULT) res = "RESULT";
> +    else if (type == PDU_PROFILE) res = "PROFILE";
> [...]
> +    else if (type == PDU_LOG_REQUEST) res = "LOG_REQUEST";
> +    else if (type == PDU_AUTH) res = "AUTH";
> +    else if (type == -1) res = "NO";
> +    if (res == "")
> +      return sprint ("TYPE-",type,"?");
> +    else
> +      return res
> +}

(Why not a lookup table, initialized in a probe begin?)


> +probe process("/usr/local/libexec/pcp/bin/pmcd").mark("PMCD")

That kind of path specificity and hard-coded action hallmarks an
end-user script rather than a tapset fragment.


> +  # Use the same naming scheme as trace.c::tracebuf
> +  t_type = $arg1
> +  t_who  = $arg2
> +  t_p1   = $arg3
> +  t_p2   = $arg4
> +
> +  if (t_type == TR_ADD_CLIENT)
> +    printf("New client: [%d]\n", t_who)
> +  else if (t_type == TR_DEL_CLIENT)
> +    printf("End client: fd=%d err=%d\n", t_who, t_p1)
> +  else if (t_type == TR_ADD_AGENT)
> +    {
> +      printf("Add PMDA: domain=%d ", t_who)
> +      if ($arg2 == -1 && $arg3 == -1)
> +     printf("DSO\n")
> +      else
> +      printf("infd=%d, outfd=%d\n", t_p1, t_p2)
> +    }
> +  else if (t_type == TR_DEL_AGENT)
> +    {
> +      printf("Drop PMDA: domain=%d ", t_who)
> +      if (t_p1 == -1 && t_p2 == -1)
> +     printf("DSO\n")
> +      else
> +     printf("infd=%d, outfd=%d\n", t_p1, t_p2)
> +    }
> +  else if (t_type == TR_EOF)
> +    printf("Premature EOF: expecting %s PDU, fd=%d\n", pdu_type(t_p1), t_who)
> +  else if (t_type == TR_WRONG_PDU)
> +    {
> +      printf("Wrong PDU type: expecting %s PDU, fd=%d, ", pdu_type(t_p1), 
> t_who)
> +      if ($arg3 > 0)
> +     printf("got %s PDU\n", pdu_type(t_p2))
> +      else if ($arg3 == 0)
> +     printf("got EOF\n")
> +      else
> +     printf("got err=%d\n", t_p2)
> +    }
> +  else if (t_type == TR_XMIT_ERR)
> +    printf("Send %s PDU failed: fd=%d, err=%d\n", pdu_type(t_p1), t_who, 
> t_p2)
> +  else if (t_type == TR_RECV_TIMEOUT)
> +    printf("Recv timeout: expecting %s PDU, fd=%d\n", pdu_type(t_p1), t_who)
> +  else if (t_type == TR_RECV_ERR)
> +    printf("Recv error: expecting %s PDU, fd=%d, err=%d", pdu_type(t_p1), 
> t_who, t_p2)
> +  else if (t_type == TR_XMIT_PDU)
> +    {
> +      printf("Xmit: %s PDU, fd=%d, ", pdu_type(t_p1), t_who)
> +      if (t_p1 == PDU_ERROR)
> +     printf(" err=%d", t_p2)
> +      else if (t_p1 == PDU_RESULT)
> +     printf("numpmid=%d", t_p2)
> +      else if (t_p1 == PDU_TEXT || t_p1 == PDU_TEXT_REQ)
> +     printf("id=%#lx", t_p2)
> +      else if (t_p1 == PDU_DESC || t_p1 == PDU_DESC_REQ)
> +     {
> +       domain = pmid_domain (t_p2)
> +       cluster = pmid_cluster (t_p2)
> +       item = pmid_item (t_p2)
> +       if (t_p2 == PM_ID_NULL)
> +           printf("pmid=PM_ID_NULL")
> +       else if (domain == DYNAMIC_PMID && item == 0)
> +         printf("pmid=%d.*.*", t_p2)
> +       else
> +         printf("pmid=%d.%d.%d", domain, cluster, item)
> +     }
> +      else if (t_p1 == PDU_INSTANCE_REQ || t_p1 == PDU_INSTANCE)
> +     {
> +       domain = pmInDom_domain (t_p2);
> +       serial = pmInDom_serial (t_p2);
> +       if (t_p2 == PM_INDOM_NULL)
> +         printf("pmid=PM_INDOM_NULL")
> +       else
> +         printf("indom=%d.%d", domain, serial)
> +     }
> +      else if (t_who == PDU_PMNS_NAMES)
> +     printf("numpmid=%d", t_p2)
> +      else if (t_who == PDU_PMNS_IDS)
> +     printf("numpmid=%d", t_p2)
> +      else if (t_who == PDU_CREDS)
> +     printf("numcreds=%d", t_p2)
> +      printf("\n")
> +    }
> +  else if (t_type == TR_RECV_PDU)
> +    printf("Recv: %s PDU, fd=%d, pdubuf=%#x\n", pdu_type(t_p1), t_who, t_p2)
> +}

BTW, the logic encoded in these nested ifs could be represented within
a tapset as a family of probe aliases:

probe pmcd.add_client = process("/PATH/to/pmcd").mark("pmcd") {
      if ($arg1 != TR_ADD_CLIENT) next;
      fd = $arg2
      description = sprintf("Add client [%d]", $arg2)
}
probe pmcd.del_client = process("/PATH/to/pmcd").mark("pmcd") {
      if ($arg1 != TR_DEL_CLIENT) next;
      name = "End client" 
      fd = $arg2
      err = $arg3
      argstr = sprintf("fd=%d err=%d", $arg2, $arg3)
}
...etc...


Then a basic end-user script can look thus:

  probe pmcd.* { println(name," ",argstr) }


- FChE

<Prev in Thread] Current Thread [Next in Thread>