/*
 * %CopyrightBegin%
 *
 * Copyright Dustin Sallings, Michal Ptaszek, Scott Lystig Fritchie 2011-2012.
 * All Rights Reserved.
 *
 * The contents of this file are subject to the Erlang Public License,
 * Version 1.1, (the "License"); you may not use this file except in
 * compliance with the License. You should have received a copy of the
 * Erlang Public License along with this software. If not, it can be
 * retrieved online at http://www.erlang.org/.
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * %CopyrightEnd%
 */

/*
 * A note on probe naming: if "__" appears in a provider probe
 * definition, then two things happen during compilation:
 *
 *    1. The "__" will turn into a hypen, "-", for the probe name.
 *    2. The "__" will turn into a single underscore, "_", for the
 *       macro names and function definitions that the compiler and
 *       C developers will see.
 *
 * We'll try to use the following naming convention.  We're a bit
 * limited because, as a USDT probe, we can only specify the 4th part
 * of the probe name, e.g. erlang*:::mumble.  The 2nd part of the
 * probe name is always going to be "beam" or "beam.smp", and the 3rd
 * part of the probe name will always be the name of the function
 * that's calling the probe.
 *
 * So, all probes will be have names defined in this file using the
 * convention category__name or category__sub_category__name.  This
 * will translate to probe names of category-name or
 * category-sub_category-name.
 *
 * Each of "category", "sub_category", and "name" may have underscores
 * but may not have hyphens.
 */

provider erlang {
    /**
     * Fired when a message is sent from one local process to another.
     *
     * NOTE: The 'size' parameter is in machine-dependent words and
     *       that the actual size of any binary terms in the message
     *       are not included.
     *
     * @param sender the PID (string form) of the sender
     * @param receiver the PID (string form) of the receiver
     * @param size the size of the message being delivered (words)
     * @param token_label for the sender's sequential trace token
     * @param token_previous count for the sender's sequential trace token
     * @param token_current count for the sender's sequential trace token
     */
    probe message__send(char *sender, char *receiver, uint32_t size,
                        int token_label, int token_previous, int token_current);

    /**
     * Fired when a message is sent from a local process to a remote process.
     *
     * NOTE: The 'size' parameter is in machine-dependent words and
     *       that the actual size of any binary terms in the message
     *       are not included.
     *
     * @param sender the PID (string form) of the sender
     * @param node_name the Erlang node name (string form) of the receiver
     * @param receiver the PID/name (string form) of the receiver
     * @param size the size of the message being delivered (words)
     * @param token_label for the sender's sequential trace token
     * @param token_previous count for the sender's sequential trace token
     * @param token_current count for the sender's sequential trace token
     */
    probe message__send__remote(char *sender, char *node_name, char *receiver,
                                uint32_t size,
                        int token_label, int token_previous, int token_current);

    /**
     * Fired when a message is queued to a local process.  This probe
     * will not fire if the sender's pid == receiver's pid.
     *
     * NOTE: The 'size' parameter is in machine-dependent words and
     *       that the actual size of any binary terms in the message
     *       are not included.
     *
     * NOTE: In cases of messages in external format (i.e. from another
     *       Erlang node), we probably don't know the message size
     *       without performing substantial extra computation.  To
     *       avoid the extra CPU overhead, the message size may be
     *       reported as -1, which can appear to a D script as 4294967295.
     *
     * @param receiver the PID (string form) of the receiver
     * @param size the size of the message being delivered (words)
     * @param queue_len length of the queue of the receiving process
     * @param token_label for the sender's sequential trace token
     * @param token_previous count for the sender's sequential trace token
     * @param token_current count for the sender's sequential trace token
     */
    probe message__queued(char *receiver, uint32_t size, uint32_t queue_len,
                        int token_label, int token_previous, int token_current);

    /**
     * Fired when a message is 'receive'd by a local process and removed
     * from its mailbox.
     *
     * NOTE: The 'size' parameter is in machine-dependent words and
     *       that the actual size of any binary terms in the message
     *       are not included.
     *
     * NOTE: In cases of messages in external format (i.e. from another
     *       Erlang node), we probably don't know the message size
     *       without performing substantial extra computation.  To
     *       avoid the extra CPU overhead, the message size may be
     *       reported as -1, which can appear to a D script as 4294967295.
     *
     * @param receiver the PID (string form) of the receiver
     * @param size the size of the message being delivered (words)
     * @param queue_len length of the queue of the receiving process
     * @param token_label for the sender's sequential trace token
     * @param token_previous count for the sender's sequential trace token
     * @param token_current count for the sender's sequential trace token
     */
    probe message__receive(char *receiver, uint32_t size, uint32_t queue_len,
                        int token_label, int token_previous, int token_current);

    /**
     * Fired when an Eterm structure is being copied.
     *
     * NOTE: Due to the placement of this probe, the process ID of
     *       owner of the Eterm is not available.
     *
     * @param size the size of the structure
     */
    probe copy__struct(uint32_t size);

    /**
     * Fired when an Eterm is being copied onto a process.
     *
     * @param proc the PID (string form) of the recipient process
     * @param size the size of the structure
     */
    probe copy__object(char *proc, uint32_t size);

    /* PID, Module, Function, Arity */

    /**
     * Fired whenever a user function is being called locally.
     *
     * @param p the PID (string form) of the process
     * @param mfa the m:f/a of the function
     * @param depth the stack depth
     */
    probe local__function__entry(char *p, char *mfa, int depth);

    /**
     * Fired whenever a user function is called externally
     * (through an export entry).
     *
     * @param p the PID (string form) of the process
     * @param mfa the m:f/a of the function
     * @param depth the stack depth
     */
    probe global__function__entry(char *p, char *mfa, int depth);

    /**
     * Fired whenever a user function returns.
     *
     * @param p the PID (string form) of the process
     * @param mfa the m:f/a of the function
     * @param depth the stack depth
     */
    probe function__return(char *p, char *mfa, int depth);

    /**
     * Fired whenever a Built In Function is called.
     *
     * @param p the PID (string form) of the process
     * @param mfa the m:f/a of the function
     */
    probe bif__entry(char *p, char *mfa);

    /**
     * Fired whenever a Built In Function returns.
     *
     * @param p the PID (string form) of the process
     * @param mfa the m:f/a of the function
     */
    probe bif__return(char *p, char *mfa);

    /**
     * Fired whenever a Native Function is called.
     *
     * @param p the PID (string form) of the process
     * @param mfa the m:f/a of the function
     */
    probe nif__entry(char *p, char *mfa);

    /**
     * Fired whenever a Native Function returns.
     *
     * @param p the PID (string form) of the process
     * @param mfa the m:f/a of the function
     */
    probe nif__return(char *p, char *mfa);

    /**
     * Fired when a major GC is starting.
     *
     * @param p the PID (string form) of the exiting process
     * @param need the number of words needed on the heap
     */
    probe gc_major__start(char *p, int need);

    /**
     * Fired when a minor GC is starting.
     *
     * @param p the PID (string form) of the exiting process
     * @param need the number of words needed on the heap
     */
    probe gc_minor__start(char *p, int need);

    /**
     * Fired when a major GC is starting.
     *
     * @param p the PID (string form) of the exiting process
     * @param reclaimed the amount of space reclaimed
     */
    probe gc_major__end(char *p, int reclaimed);

    /**
     * Fired when a minor GC is starting.
     *
     * @param p the PID (string form) of the exiting process
     * @param reclaimed the amount of space reclaimed
     */
    probe gc_minor__end(char *p, int reclaimed);

    /**
     * Fired when a process is spawned.
     *
     * @param p the PID (string form) of the new process.
     * @param mfa the m:f/a of the function
     */
    probe process__spawn(char *p, char *mfa);

    /**
     * Fired when a process is exiting.
     *
     * @param p the PID (string form) of the exiting process
     * @param reason the reason for the exit (may be truncated)
     */
    probe process__exit(char *p, char *reason);

    /**
     * Fired when exit signal is delivered to a local process.
     *
     * @param sender the PID (string form) of the exiting process
     * @param receiver the PID (string form) of the process receiving EXIT signal
     * @param reason the reason for the exit (may be truncated)
     */
    probe process__exit_signal(char *sender, char *receiver, char *reason);

    /**
     * Fired when exit signal is delivered to a remote process.
     *
     * @param sender the PID (string form) of the exiting process
     * @param node_name the Erlang node name (string form) of the receiver
     * @param receiver the PID (string form) of the process receiving EXIT signal
     * @param reason the reason for the exit (may be truncated)
     * @param token_label for the sender's sequential trace token
     * @param token_previous count for the sender's sequential trace token
     * @param token_current count for the sender's sequential trace token
     */
    probe process__exit_signal__remote(char *sender, char *node_name,
                                       char *receiver, char *reason,
                        int token_label, int token_previous, int token_current);

    /**
     * Fired when a process is scheduled.
     *
     * @param p the PID (string form) of the newly scheduled process
     * @param mfa the m:f/a of the function it should run next
     */
    probe process__scheduled(char *p, char *mfa);

    /**
     * Fired when a process is unscheduled.
     *
     * @param p the PID (string form) of the process that has been
     * unscheduled.
     */
    probe process__unscheduled(char *p);

    /**
     * Fired when a process goes into hibernation.
     *
     * @param p the PID (string form) of the process entering hibernation
     * @param mfa the m:f/a of the location to resume
     */
    probe process__hibernate(char *p, char *mfa);

    /**
     * Fired when a process is unblocked after a port has been unblocked.
     *
     * @param p the PID (string form) of the process that has been
     * unscheduled.
     * @param port the port that is no longer busy (i.e., is now unblocked)
     */
    probe process__port_unblocked(char *p, char *port);

    /**
     * Fired when process' heap is growing.
     *
     * @param p the PID (string form)
     * @param old_size the size of the old heap
     * @param new_size the size of the new heap
     */
    probe process__heap_grow(char *p, int old_size, int new_size);

    /**
     * Fired when process' heap is shrinking.
     *
     * @param p the PID (string form)
     * @param old_size the size of the old heap
     * @param new_size the size of the new heap
     */
    probe process__heap_shrink(char *p, int old_size, int new_size);

    /* network distribution */

    /**
     * Fired when network distribution event monitor events are triggered.
     *
     * @param node the name of the reporting node
     * @param what the type of event, e.g., nodeup, nodedown
     * @param monitored_node the name of the monitored node
     * @param type the type of node, e.g., visible, hidden
     * @param reason the reason term, e.g., normal, connection_closed, term()
     */
    probe dist__monitor(char *node, char *what, char *monitored_node,
                        char *type, char *reason);

    /**
     * Fired when network distribution port is busy (i.e. blocked),
     * usually due to the remote node not consuming distribution
     * data quickly enough.
     *
     * @param node the name of the reporting node
     * @param port the port ID of the busy port
     * @param remote_node the name of the remote node.
     * @param pid the PID (string form) of the local process that has
     *        become unschedulable until the port becomes unblocked.
     */
    probe dist__port_busy(char *node, char *port, char *remote_node,
                          char *pid);

    /**
     * Fired when network distribution's driver's "output" callback is called
     *
     * @param node the name of the reporting node
     * @param port the port ID of the busy port
     * @param remote_node the name of the remote node.
     * @param bytes the number of bytes written
     */
    probe dist__output(char *node, char *port, char *remote_node, int bytes);

    /**
     * Fired when network distribution's driver's "outputv" callback is called
     *
     * @param node the name of the reporting node
     * @param port the port ID of the busy port
     * @param remote_node the name of the remote node.
     * @param bytes the number of bytes written
     */
    probe dist__outputv(char *node, char *port, char *remote_node, int bytes);

    /**
     * Fired when network distribution port is no longer busy (i.e. blocked).
     *
     * NOTE: This probe may fire multiple times after the same single
     *       dist-port_busy probe firing.
     *
     * @param node the name of the reporting node
     * @param port the port ID of the busy port
     * @param remote_node the name of the remote node.
     */
    probe dist__port_not_busy(char *node, char *port, char *remote_node);

    /* ports */

    /**
     * Fired when new port is opened.
     *
     * @param process the PID (string form)
     * @param port_name the string used when the port was opened
     * @param port the Port (string form) of the new port
     */
    probe port__open(char *process, char *port_name, char *port);

    /**
     * Fired when port_command is issued.
     *
     * @param process the PID (string form)
     * @param port the Port (string form)
     * @param port_name the string used when the port was opened
     * @param command_type type of the issued command, one of: "close", "command" or "connect"
     */
    probe port__command(char *process, char *port, char *port_name, char *command_type);

    /**
     * Fired when port_control is issued.
     *
     * @param process the PID (string form)
     * @param port the Port (string form)
     * @param port_name the string used when the port was opened
     * @param command_no command number that has been issued to the port
     */
    probe port__control(char *process, char *port, char *port_name, int command_no);

    /**
     * Fired when port is closed via port_close/1 (reason = 'normal')
     * or is sent an exit signal.
     *
     * @param process the PID (string form)
     * @param port the Port (string form)
     * @param port_name the string used when the port was opened
     * @param reason Erlang term representing the exit signal, e.g. 'normal'
     */
    probe port__exit(char *process, char *port, char *port_name,
                     char *new_process);

    /**
     * Fired when port_connect is issued.
     *
     * @param process the PID (string form) of the current port owner
     * @param port the Port (string form)
     * @param port_name the string used when the port was opened
     * @param new_process the PID (string form) of the new port owner
     */
    probe port__connect(char *process, char *port, char *port_name,
                        char *new_process);

    /**
     * Fired when a port is busy (i.e. blocked)
     *
     * @param port the port ID of the busy port
     */
    probe port__busy(char *port);

    /**
     * Fired when a port is no longer busy (i.e. no longer blocked)
     *
     * @param port the port ID of the not busy port
     */
    probe port__not_busy(char *port);

    /* drivers */

    /**
     * Fired when drivers's "init" callback is called.
     *
     * @param name the name of the driver
     * @param major the major version number
     * @param minor the minor version number
     * @param flags the flags argument
     */
    probe driver__init(char *name, int major, int minor, int flags);

    /**
     * Fired when drivers's "start" callback is called.
     *
     * @param process the PID (string form) of the calling process
     * @param name the name of the driver
     * @param port the Port (string form) of the driver's port
     */
     probe driver__start(char *process, char *name, char *port);

    /**
     * Fired when drivers's "stop" callback is called.
     *
     * @param process the PID (string form) of the calling process
     * @param name the name of the driver
     * @param port the Port (string form) of the driver's port
     */
     probe driver__stop(char *process, char *name, char *port);

    /**
     * Fired when drivers's "finish" callback is called.
     *
     * @param name the name of the driver
     */
     probe driver__finish(char *name);

    /**
     * Fired when drivers's "flush" callback is called.
     *
     * @param process the PID (string form)
     * @param port the Port (string form)
     * @param port_name the string used when the port was opened
     */
    probe driver__flush(char *process, char *port, char *port_name);

    /**
     * Fired when driver's "output" callback is called
     *
     * @param process the PID (string form)
     * @param port the Port (string form)
     * @param port_name the string used when the port was opened
     * @param bytes the number of bytes written
     */
    probe driver__output(char *node, char *port, char *port_name, int bytes);

    /**
     * Fired when driver's "outputv" callback is called
     *
     * @param process the PID (string form)
     * @param port the Port (string form)
     * @param port_name the string used when the port was opened
     * @param bytes the number of bytes written
     */
    probe driver__outputv(char *node, char *port, char *port_name, int bytes);

    /**
     * Fired when driver's "control" callback is called
     *
     * @param process the PID (string form)
     * @param port the Port (string form)
     * @param port_name the string used when the port was opened
     * @param command the command #
     * @param bytes the number of bytes written
     */
    probe driver__control(char *node, char *port, char *port_name,
                          int command, int bytes);

    /**
     * Fired when driver's "call" callback is called
     *
     * @param process the PID (string form)
     * @param port the Port (string form)
     * @param port_name the string used when the port was opened
     * @param command the command #
     * @param bytes the number of bytes written
     */
    probe driver__call(char *node, char *port, char *port_name,
                       int command, int bytes);

    /**
     * Fired when driver's "event" callback is called
     *
     * @param process the PID (string form)
     * @param port the Port (string form)
     * @param port_name the string used when the port was opened
     */
    probe driver__event(char *node, char *port, char *port_name);

    /**
     * Fired when driver's "ready_input" callback is called
     *
     * @param process the PID (string form)
     * @param port the Port (string form)
     * @param port_name the string used when the port was opened
     */
    probe driver__ready_input(char *node, char *port, char *port_name);

    /**
     * Fired when driver's "read_output" callback is called
     *
     * @param process the PID (string form)
     * @param port the Port (string form)
     * @param port_name the string used when the port was opened
     */
    probe driver__ready_output(char *node, char *port, char *port_name);

    /**
     * Fired when driver's "timeout" callback is called
     *
     * @param process the PID (string form)
     * @param port the Port (string form)
     * @param port_name the string used when the port was opened
     */
    probe driver__timeout(char *node, char *port, char *port_name);

    /**
     * Fired when drivers's "ready_async" callback is called.
     *
     * @param process the PID (string form)
     * @param port the Port (string form)
     * @param port_name the string used when the port was opened
     */
    probe driver__ready_async(char *process, char *port, char *port_name);

    /**
     * Fired when driver's "process_exit" callback is called
     *
     * @param process the PID (string form)
     * @param port the Port (string form)
     * @param port_name the string used when the port was opened
     */
    probe driver__process_exit(char *node, char *port, char *port_name);

    /**
     * Fired when driver's "stop_select" callback is called
     *
     * @param name the name of the driver
     */
    probe driver__stop_select(char *name);


    /* Async driver pool */

    /**
     * Show the post-add length of the async driver thread pool member's queue.
     *
     * NOTE: The port name is not available: additional lock(s) must
     *       be acquired in order to get the port name safely in an SMP
     *       environment.  The same is true for the aio__pool_get probe.
     *
     * @param port the Port (string form)
     * @param new queue length
     */
    probe aio_pool__add(char *, int);

    /**
     * Show the post-get length of the async driver thread pool member's queue.
     *
     * @param port the Port (string form)
     * @param new queue length
     */
    probe aio_pool__get(char *, int);

    /* Probes for efile_drv.c */

    /**
     * Entry into the efile_drv.c file I/O driver
     *
     * For a list of command numbers used by this driver, see the section
     * "Guide to probe arguments" in ../../../README.md.  That section
     * also contains explanation of the various integer and string
     * arguments that may be present when any particular probe fires.
     *
     * NOTE: Not all Linux platforms (using SystemTap) can support
     *       arguments beyond arg9.
     *
     *
     * TODO: Adding the port string, args[10], is a pain.  Making that
     *       port string available to all the other efile_drv.c probes
     *       will be more pain.  Is the pain worth it?  If yes, then
     *       add them everywhere else and grit our teeth.  If no, then
     *       rip it out.
     *
     * @param thread-id number of the scheduler Pthread                   arg0
     * @param tag number: {thread-id, tag} uniquely names a driver operation
     * @param user-tag string                                             arg2
     * @param command number                                              arg3
     * @param string argument 1                                           arg4
     * @param string argument 2                                           arg5
     * @param integer argument 1                                          arg6
     * @param integer argument 2                                          arg7
     * @param integer argument 3                                          arg8
     * @param integer argument 4                                          arg9
     * @param port the port ID of the busy port                       args[10]
     */
    probe efile_drv__entry(int, int, char *, int, char *, char *,
                           int64_t, int64_t, int64_t, int64_t, char *);

    /**
     * Entry into the driver's internal work function.  Computation here
     * is performed by a async worker pool Pthread.
     *
     * @param thread-id number
     * @param tag number
     * @param command number
     */
    probe efile_drv__int_entry(int, int, int);

    /**
     * Return from the driver's internal work function.
     *
     * @param thread-id number
     * @param tag number
     * @param command number
     */
    probe efile_drv__int_return(int, int, int);

    /**
     * Return from the efile_drv.c file I/O driver
     *
     * @param thread-id number                                            arg0
     * @param tag number                                                  arg1
     * @param user-tag string                                             arg2
     * @param command number                                              arg3
     * @param Success? 1 is success, 0 is failure                         arg4
     * @param If failure, the errno of the error.                         arg5
     */
    probe efile_drv__return(int, int, char *, int, int, int);

/*
 * NOTE:
 * For formatting int64_t arguments within a D script, see:
 *
 *   http://mail.opensolaris.org/pipermail/dtrace-discuss/2006-November/002830.html
 *   Summary:
 *       "1) you don't need the 'l' printf() modifiers with DTrace ever"
 */

/*
 * NOTE: For file_drv_return + SMP + R14B03 (and perhaps other
 *       releases), the sched-thread-id will be the same as the
 *       work-thread-id: erl_async.c's async_main() function
 *       will call the asynchronous invoke function and then
 *       immediately call the drivers ready_async function while
 *       inside the same I/O worker pool thread.
 *       For R14B03's source, see erl_async.c lines 302-317.
 */
};

#pragma D attributes Evolving/Evolving/Common provider erlang provider
#pragma D attributes Private/Private/Common provider erlang module
#pragma D attributes Private/Private/Common provider erlang function
#pragma D attributes Evolving/Evolving/Common provider erlang name
#pragma D attributes Evolving/Evolving/Common provider erlang args