// Performance CoPilot pmcd tapset
// Copyright (C) 2013 Red Hat Inc.
//
// This file is part of SystemTap, and is free software.  You can
// redistribute it and/or modify it under the terms of the GNU General
// Public License (GPL); either version 2, or (at your option) any
// later version.


global pmid_domain[2]
global pmid_cluster[2]
global pmid_item[2]
global pmindom_domain[2]
global pmindom_serial[2]
global pdu_types[25]

# from impl.h
global PDU_ERROR	=	0x7000
global PDU_RESULT	=	0x7001
global PDU_PROFILE	=	0x7002
global PDU_FETCH	=	0x7003
global PDU_DESC_REQ	=	0x7004
global PDU_DESC		=	0x7005
global PDU_INSTANCE_REQ	=	0x7006
global PDU_INSTANCE	=	0x7007
global PDU_TEXT_REQ	=	0x7008
global PDU_TEXT		=	0x7009
global PDU_CONTROL_REQ	=	0x700a
global PDU_CREDS	=	0x700c
global PDU_PMNS_IDS	=	0x700d
global PDU_PMNS_NAMES	=	0x700e
global PDU_PMNS_CHILD	=	0x700f
global PDU_PMNS_TRAVERSE =	0x7010
global PDU_ATTR		=	0x7011
global PDU_LOG_CONTROL	=	0x8000
global PDU_LOG_STATUS	=	0x8001
global PDU_LOG_REQUEST	=	0x8002
global DYNAMIC_PMID	=	511
global PM_INDOM_NULL	=	0xffffffff
global PM_ID_NULL	=	0xffffffff

# from pmcd.h
global TR_ADD_CLIENT	=	1
global TR_DEL_CLIENT	=	2
global TR_ADD_AGENT	=	3
global TR_DEL_AGENT	=	4
global TR_EOF		=	5
global TR_XMIT_PDU	=	7
global TR_RECV_PDU	=	8
global TR_WRONG_PDU	=	9
global TR_XMIT_ERR	=	10
global TR_RECV_TIMEOUT	=	11
global TR_RECV_ERR	=	12

# Use the same naming scheme as trace.c::tracebuf
# We sadly eschew stap 2.0+ feature to make $argN more symbolic:
# @define t_type %( $arg1 %)
# @define t_who %( $arg2 %)
# @define t_p1 %( $arg3 %)
# @define t_p2 %( $arg4 %)


probe begin {
  // [0]=bit start position [1]=bit length
    pmid_item[0] = 0
    pmid_item[1] = 10
    pmid_cluster[0] = 10
    pmid_cluster[1] = 12
    pmid_domain[0] = 22
    pmid_domain[1] = 9
    pmindom_serial[0] = 0
    pmindom_serial[1] = 22
    pmindom_domain[0] = 22
    pmindom_domain[1] = 9

    pdu_types[PDU_ERROR] = "ERROR";
    pdu_types[PDU_RESULT] = "RESULT";
    pdu_types[PDU_PROFILE] = "PROFILE";
    pdu_types[PDU_FETCH] = "FETCH";
    pdu_types[PDU_DESC_REQ] = "DESC_REQ";
    pdu_types[PDU_DESC] = "DESC";
    pdu_types[PDU_INSTANCE_REQ] = "INSTANCE_REQ";
    pdu_types[PDU_INSTANCE] = "INSTANCE";
    pdu_types[PDU_TEXT_REQ] = "TEXT_REQ";
    pdu_types[PDU_TEXT] = "TEXT";
    pdu_types[PDU_CONTROL_REQ] = "CONTROL_REQ";
    pdu_types[PDU_CREDS] = "CREDS";
    pdu_types[PDU_PMNS_IDS] = "PMNS_IDS";
    pdu_types[PDU_PMNS_NAMES] = "PMNS_NAMES";
    pdu_types[PDU_PMNS_CHILD] = "PMNS_CHILD";
    pdu_types[PDU_PMNS_TRAVERSE] = "PMNS_TRAVERSE";
    pdu_types[PDU_ATTR] = "ATTR";
    pdu_types[PDU_LOG_CONTROL] = "LOG_CONTROL";
    pdu_types[PDU_LOG_STATUS] = "LOG_STATUS";
    pdu_types[PDU_LOG_REQUEST] = "LOG_REQUEST";
    pdu_types[-1] = "NO";
}


function bitextract (bits, start, length)
{
  // HAVE_BITFIELDS_LTOR ?
    %( arch != "i386" && arch != "x86_64" %?
       // HAVE_BITFIELDS_RTOL
       start = 32 - start - length;
    %)
    return ((bits >> start) & ((1 << length) - 1));
}  

function pdu_type:string (type:long) 
{
    if (type in pdu_types)
	return pdu_types[type]
    else
	return sprint ("TYPE-",type,"?");
}


/**
 * probe pmcd.* - Indicates the type of the pmcd trace
 *
 * @t_desc:  Description of the trace
 * @t_type: Type of tracing that occurred
 * @t_who: File descriptor
 * @t_p1: Trace argument 1
 * @t_p2: Trace argument 2
 */


probe pmcd.add_client = process("@path@").mark("PMCD")
{
    t_desc = ""

    if ($arg1 != TR_ADD_CLIENT)
	next
    t_desc = sprintf("New client: [%d]", $arg2)
}


probe pmcd.del_client = process("@path@").mark("PMCD")
{
    t_desc = ""

    if ($arg1 != TR_DEL_CLIENT)
	next
    t_desc = sprintf("End client: fd=%d err=%d", $arg2, $arg3)
}


probe pmcd.add_agent = process("@path@").mark("PMCD")
{
    t_desc = ""

    if ($arg1 != TR_ADD_AGENT)
	next
    t_desc = sprintf("Add PMDA: domain=%d ", $arg2)
    if ($arg3 == -1 && $arg4 == -1)
	t_desc = t_desc . "DSO"
    else
	t_desc = t_desc . sprintf("infd=%d, outfd=%d", $arg3, $arg4)
}


probe pmcd.del_agent = process("@path@").mark("PMCD")
{
    t_desc = ""

    if ($arg1 != TR_DEL_AGENT)
	next

    t_desc = sprintf("Drop PMDA: domain=%d ", $arg2)
    if ($arg3 == -1 && $arg4 == -1)
	t_desc = t_desc . "DSO"
    else
	t_desc = t_desc . sprintf("infd=%d, outfd=%d", $arg3, $arg4)
}


probe pmcd.eof = process("@path@").mark("PMCD")
{
    t_desc = ""

    if ($arg1 != TR_EOF)
	next;
  
    t_desc = sprintf("Premature EOF: expecting %s PDU, fd=%d", pdu_type($arg3), $arg2)
}


probe pmcd.wrong_pdu = process("@path@").mark("PMCD")
{
    t_desc = ""

    if ($arg1 != TR_WRONG_PDU)
	next

    t_desc = sprintf("Wrong PDU type: expecting %s PDU, fd=%d, ", pdu_type($arg3), $arg2)
    if ($arg3 > 0)
	t_desc = t_desc . sprintf("got %s PDU", pdu_type($arg4))
    else if ($arg3 == 0)
	t_desc = t_desc . "got EOF"
    else
	t_desc = t_desc . sprintf("got err=%d", $arg4)
}


probe pmcd.xmit_err = process("@path@").mark("PMCD")
{
    t_desc = ""

    if ($arg1 != TR_XMIT_ERR)
	next

    t_desc = sprintf("Send %s PDU failed: fd=%d, err=%d", pdu_type($arg3), $arg2, $arg4)
}


probe pmcd.recv_timeout = process("@path@").mark("PMCD")
{
    t_desc = ""

    if ($arg1 != TR_RECV_TIMEOUT)
	next

    t_desc = sprintf("Recv timeout: expecting %s PDU, fd=%d", pdu_type($arg3), $arg2)
}


probe pmcd.recv_err = process("@path@").mark("PMCD")
{
    t_desc = ""

    if ($arg1 != TR_RECV_ERR)
	next;

    t_desc = sprintf("Recv error: expecting %s PDU, fd=%d, err=%d", pdu_type($arg3), $arg2, $arg4)
}


probe pmcd.pdu.xmit = process("@path@").mark("PMCD_PDU")
{
    t_desc = ""

    if ($arg1 != TR_XMIT_PDU)
	next

    t_desc = sprintf("Xmit: %s PDU, fd=%d, ", pdu_type($arg3), $arg2)
    if ($arg3 == PDU_ERROR)
	t_desc = t_desc . sprintf(" err=%d", $arg4)
    else if ($arg3 == PDU_RESULT)
	t_desc = t_desc . sprintf("numpmid=%d", $arg4)
    else if ($arg3 == PDU_TEXT || $arg3 == PDU_TEXT_REQ)
	t_desc = t_desc . sprintf("id=%#lx", $arg4)
    else if ($arg3 == PDU_DESC || $arg3 == PDU_DESC_REQ)
    {
	domain = bitextract ($arg4, pmid_domain[0],pmid_domain[1])
	cluster = bitextract ($arg4, pmid_cluster[0],pmid_cluster[1])
	item = bitextract ($arg4, pmid_item[0],pmid_item[1])
	if ($arg4 == PM_ID_NULL)
	    t_desc = t_desc . sprintf("pmid=PM_ID_NULL")
	else if (domain == DYNAMIC_PMID && item == 0)
	    t_desc = t_desc . sprintf("pmid=%d.*.*", $arg4)
	else
	    t_desc = t_desc . sprintf("pmid=%d.%d.%d", domain, cluster, item)
    }
    else if ($arg3 == PDU_INSTANCE_REQ || $arg3 == PDU_INSTANCE)
    {
	domain = bitextract ($arg4, pmindom_domain[0],pmindom_domain[1])
	serial = bitextract ($arg4, pmindom_serial[0],pmindom_serial[1])
	if ($arg4 == PM_INDOM_NULL)
	    t_desc = t_desc . sprintf("pmid=PM_INDOM_NULL")
	else
	    t_desc = t_desc . sprintf("indom=%d.%d", domain, serial)
    }
    else if ($arg2 == PDU_PMNS_NAMES)
	t_desc = t_desc . sprintf("numpmid=%d", $arg4)
    else if ($arg2 == PDU_PMNS_IDS)
	t_desc = t_desc . sprintf("numpmid=%d", $arg4)
    else if ($arg2 == PDU_CREDS)
	t_desc = t_desc . sprintf("numcreds=%d", $arg4)
}


probe pmcd.pdu.recv = process("@path@").mark("PMCD_PDU")
{
    t_desc = ""

    if ($arg1 != TR_RECV_PDU)
	next;

    t_desc = sprintf("Recv: %s PDU, fd=%d, pdubuf=%#x", pdu_type($arg3), $arg2, $arg4)
}


// Example Use
# probe pmcd.* 
# {
#     println(t_desc)
# }

# probe pmcd.pdu.* 
# {
#     println(t_desc)
# }
