|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* (C) Copyright IBM Corp. 2006
|
|
Packit |
577717 |
* Contributed by Kevin Corry <kevcorry@us.ibm.com>
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
Packit |
577717 |
* copy of this software and associated documentation files (the "Software"),
|
|
Packit |
577717 |
* to deal in the Software without restriction, including without limitation
|
|
Packit |
577717 |
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
Packit |
577717 |
* and/or sellcopies of the Software, and to permit persons to whom the
|
|
Packit |
577717 |
* Software is furnished to do so, subject to the following conditions:
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* The above copyright notice and this permission notice shall be included
|
|
Packit |
577717 |
* in all copies or substantial portions of the Software.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
Packit |
577717 |
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
Packit |
577717 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
Packit |
577717 |
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
Packit |
577717 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
Packit |
577717 |
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
Packit |
577717 |
* DEALINGS IN THE SOFTWARE.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* pfmsetup
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Very simple command-line tool to drive the perfmon2 kernel API. Inspired
|
|
Packit |
577717 |
* by the dmsetup tool from device-mapper.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Compile with:
|
|
Packit |
577717 |
* gcc -Wall -o pfmsetup pfmsetup.c -lpfm
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Run with:
|
|
Packit |
577717 |
* pfmsetup <command_file>
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Available commands for the command_file:
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* create_context [options] <context_id>
|
|
Packit |
577717 |
* Create a new context for accessing the performance counters. Each new
|
|
Packit |
577717 |
* context automatically gets one event-set with an ID of 0.
|
|
Packit |
577717 |
* - options: --system
|
|
Packit |
577717 |
* --no-overflow-msg
|
|
Packit |
577717 |
* --block-on-notify
|
|
Packit |
577717 |
* --sampler <sampler_name>
|
|
Packit |
577717 |
* - <context_id>: specify an integer that you want to associate with
|
|
Packit |
577717 |
* the new context for use in other commands.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* load_context <context_id> <event_set_id> <program_id|cpu_id>
|
|
Packit |
577717 |
* Attach the specified context and event-set to the specified program.
|
|
Packit |
577717 |
* - <context_id>: ID that you specified when creating the context.
|
|
Packit |
577717 |
* - <event_set_id>: ID that you specified when creating an event-set
|
|
Packit |
577717 |
* within the given context. All contexts automatically
|
|
Packit |
577717 |
* have an event-set with ID of 0.
|
|
Packit |
577717 |
* - <program_id|cpu_id>: ID that you specified when starting a program
|
|
Packit |
577717 |
* with the run_program command, or the number of
|
|
Packit |
577717 |
* the CPU to attach to for system-wide mode.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* unload_context <context_id>
|
|
Packit |
577717 |
* Detach the specified context from the program that it's currently
|
|
Packit |
577717 |
* attached to.
|
|
Packit |
577717 |
* - <context_id>: ID that you specified when creating the context.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* close_context <context_id>
|
|
Packit |
577717 |
* Clean up the specified context. After this call, the context_id will no
|
|
Packit |
577717 |
* longer be valid.
|
|
Packit |
577717 |
* - <context_id>: ID that you specified when creating the context.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* write_pmc <context_id> <event_set_id> <<pmc_id> <pmc_value>>+
|
|
Packit |
577717 |
* Write one or more control register values.
|
|
Packit |
577717 |
* - <context_id>: ID that you specified when creating the context.
|
|
Packit |
577717 |
* - <event_set_id>: ID that you specified when creating an event-set
|
|
Packit |
577717 |
* within the given context. All contexts automatically
|
|
Packit |
577717 |
* have an event-set with ID of 0.
|
|
Packit |
577717 |
* - <pmc_id>: ID of the desired control register. See the register
|
|
Packit |
577717 |
* mappings in the Perfmon kernel code to determine which
|
|
Packit |
577717 |
* PMC represents the control register you're interested in.
|
|
Packit |
577717 |
* - <pmc_value>: Value to write into the specified PMC. You need to know
|
|
Packit |
577717 |
* the exact numeric value - no translations are done from
|
|
Packit |
577717 |
* event names or masks. Multiple PMC id/value pairs can
|
|
Packit |
577717 |
* be given in one write_pmc command.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* write_pmd <context_id> <event_set_id> <<pmd_id> <pmd_value>>+
|
|
Packit |
577717 |
* Write one or more data register values.
|
|
Packit |
577717 |
* - <context_id>: ID that you specified when creating the context.
|
|
Packit |
577717 |
* - <event_set_id>: ID that you specified when creating an event-set
|
|
Packit |
577717 |
* within the given context. All contexts automatically
|
|
Packit |
577717 |
* have an event-set with ID of 0.
|
|
Packit |
577717 |
* - <pmd_id>: ID of the desired data register. See the register
|
|
Packit |
577717 |
* mappings in the Perfmon kernel code to determine which
|
|
Packit |
577717 |
* PMD represents the control register you're interested in.
|
|
Packit |
577717 |
* - <pmd_value>: Value to write into the specified PMD. Multiple PMD
|
|
Packit |
577717 |
* id/value pairs can be given in one write_pmd command.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* read_pmd <context_id> <event_set_id> <pmd_id>+
|
|
Packit |
577717 |
* Read one or more data register values.
|
|
Packit |
577717 |
* - <context_id>: ID that you specified when creating the context.
|
|
Packit |
577717 |
* - <event_set_id>: ID that you specified when creating an event-set
|
|
Packit |
577717 |
* within the given context. All contexts automatically
|
|
Packit |
577717 |
* have an event-set with ID of 0.
|
|
Packit |
577717 |
* - <pmd_id>: ID of the desired data register. See the register
|
|
Packit |
577717 |
* mappings in the Perfmon kernel code to determine which
|
|
Packit |
577717 |
* PMD represents the control register you're interested in.
|
|
Packit |
577717 |
* Multiple PMD IDs can be given in one read_pmd command.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* start_counting <context_id> <event_set_id>
|
|
Packit |
577717 |
* Start counting using the specified context and event-set.
|
|
Packit |
577717 |
* - <context_id>: ID that you specified when creating the context.
|
|
Packit |
577717 |
* - <event_set_id>: ID that you specified when creating an event-set
|
|
Packit |
577717 |
* within the given context. All contexts automatically
|
|
Packit |
577717 |
* have an event-set with ID of 0.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* stop_counting <context_id>
|
|
Packit |
577717 |
* Stop counting on the specified context.
|
|
Packit |
577717 |
* - <context_id>: ID that you specified when creating the context.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* restart_counting <context_id>
|
|
Packit |
577717 |
* Restart counting on the specified context.
|
|
Packit |
577717 |
* - <context_id>: ID that you specified when creating the context.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* create_eventset [options] <context_id> <event_set_id>
|
|
Packit |
577717 |
* Create a new event-set for an existing context.
|
|
Packit |
577717 |
* - options: --next-set <next_event_set_id>
|
|
Packit |
577717 |
* --timeout <nanoseconds>
|
|
Packit |
577717 |
* --switch-on-overflow
|
|
Packit |
577717 |
* --exclude-idle
|
|
Packit |
577717 |
* - <context_id>: ID that you specified when creating the context.
|
|
Packit |
577717 |
* - <event_set_id>: specify an integer that you want to associate with
|
|
Packit |
577717 |
* the new event-set for use in other commands.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* delete_eventset <context_id> <event_set_id>
|
|
Packit |
577717 |
* Delete an existing event-set from an existing context.
|
|
Packit |
577717 |
* - <context_id>: ID that you specified when creating the context.
|
|
Packit |
577717 |
* - <event_set_id>: ID that you specified when creating the event-set.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* getinfo_eventset <context_id> <event_set_id>
|
|
Packit |
577717 |
* Display information about an event-set.
|
|
Packit |
577717 |
* - <context_id>: ID that you specified when creating the context.
|
|
Packit |
577717 |
* - <event_set_id>: ID that you specified when creating the event-set.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* run_program <program_id> <program name and arguments>
|
|
Packit |
577717 |
* First step in starting a program to monitor. In order to allow time to
|
|
Packit |
577717 |
* set up the counters to monitor the program, this command only forks a
|
|
Packit |
577717 |
* child process. It then suspends itself using ptrace. You must call the
|
|
Packit |
577717 |
* resume_program command to wake up the new child process and exec the
|
|
Packit |
577717 |
* desired program.
|
|
Packit |
577717 |
* - <program_id>: Specify an integer that you want to associate with
|
|
Packit |
577717 |
* the program for use in other commands.
|
|
Packit |
577717 |
* - <program name and arguments>: Specify the program and its arguments
|
|
Packit |
577717 |
* exactly as you would on the command
|
|
Packit |
577717 |
* line.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* resume_program <program_id>
|
|
Packit |
577717 |
* When a program is 'run', a child process is forked, but the child is
|
|
Packit |
577717 |
* ptrace'd before exec'ing the specified program. This gives you time to
|
|
Packit |
577717 |
* do any necessary setup to monitor the program. This resume_program
|
|
Packit |
577717 |
* command wakes up the child process and finishes exec'ing the desired
|
|
Packit |
577717 |
* program. If a context has been loaded and started for this program,
|
|
Packit |
577717 |
* then the counters will have actually started following this command.
|
|
Packit |
577717 |
* - <program_id>: ID that you specified when starting the program.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* wait_on_program <program_id>
|
|
Packit |
577717 |
* Wait for a program to complete and exit. After this call, the program_id
|
|
Packit |
577717 |
* will no longer be valid.
|
|
Packit |
577717 |
* - <program_id>: ID that you specified when starting the program.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* sleep
|
|
Packit |
577717 |
* Sleep for the specified number of seconds. This could be used if you
|
|
Packit |
577717 |
* want to take measurements while a program is running, or if you're
|
|
Packit |
577717 |
* running a system-wide context.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Blank lines in the command file and lines starting with '#' are ignored.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Example command-file for use on an Intel P4/EM64T. This command-file creates
|
|
Packit |
577717 |
* one context, starts 'dd' to read data from /dev/sda, loads the context onto
|
|
Packit |
577717 |
* the 'dd' program, writes values into two PMCs (MSR_CRU_ESCR0 and
|
|
Packit |
577717 |
* MSR_IQ_CCCR0) in order to set up for counting retired instructions, clears
|
|
Packit |
577717 |
* one PMD (MSR_IQ_COUNTER0), starts the counters, resumes the 'dd' program,
|
|
Packit |
577717 |
* waits for it to complete, and reads the number of instructions retired from
|
|
Packit |
577717 |
* the PMD.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* create_context 1
|
|
Packit |
577717 |
* run_program 1 dd if=/dev/sda of=/dev/null bs=1M count=1024
|
|
Packit |
577717 |
* load_context 1 0 1
|
|
Packit |
577717 |
* write_pmc 1 0 20 0x0400020c 29 0x04039000
|
|
Packit |
577717 |
* write_pmd 1 0 6 0
|
|
Packit |
577717 |
* start_counting 1 0
|
|
Packit |
577717 |
* resume_program 1
|
|
Packit |
577717 |
* wait_on_program 1
|
|
Packit |
577717 |
* read_pmd 1 0 6
|
|
Packit |
577717 |
* close_context 1
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* The output will look like this:
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* pfmsetup: Created context 1 with file-descriptor 4.
|
|
Packit |
577717 |
* pfmsetup: Started program 1: 'dd'.
|
|
Packit |
577717 |
* pfmsetup: Loaded context 1, event-set 0 onto program 1.
|
|
Packit |
577717 |
* pfmsetup: Wrote to PMC 20: 0x400020c
|
|
Packit |
577717 |
* pfmsetup: Wrote to PMC 29: 0x4039000
|
|
Packit |
577717 |
* pfmsetup: Wrote to PMD 6: 0
|
|
Packit |
577717 |
* pfmsetup: Started counting for context 1, event-set 0.
|
|
Packit |
577717 |
* pfmsetup: Resumed program 1.
|
|
Packit |
577717 |
* 1024+0 records in
|
|
Packit |
577717 |
* 1024+0 records out
|
|
Packit |
577717 |
* pfmsetup: Waited for program 1 to complete.
|
|
Packit |
577717 |
* pfmsetup: Read from PMD 6: 415218111
|
|
Packit |
577717 |
* pfmsetup: Closed and freed context 1.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#include <stdio.h>
|
|
Packit |
577717 |
#include <stdlib.h>
|
|
Packit |
577717 |
#include <string.h>
|
|
Packit |
577717 |
#include <unistd.h>
|
|
Packit |
577717 |
#include <errno.h>
|
|
Packit |
577717 |
#include <getopt.h>
|
|
Packit |
577717 |
#include <inttypes.h>
|
|
Packit |
577717 |
#include <sched.h>
|
|
Packit |
577717 |
#include <sys/wait.h>
|
|
Packit |
577717 |
#include <sys/ptrace.h>
|
|
Packit |
577717 |
#include <perfmon/perfmon.h>
|
|
Packit |
577717 |
#include <perfmon/perfmon_dfl_smpl.h>
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define FALSE 0
|
|
Packit |
577717 |
#define TRUE 1
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define WHITESPACE " \t\n"
|
|
Packit |
577717 |
#define MAX_TOKENS 32
|
|
Packit |
577717 |
#define PFMSETUP_NAME "pfmsetup"
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define USAGE(f, x...) printf(PFMSETUP_NAME ": USAGE: " f "\n" , ## x)
|
|
Packit |
577717 |
#define LOG_ERROR(f, x...) printf(PFMSETUP_NAME ": Error: %s: " f "\n", __FUNCTION__ , ## x)
|
|
Packit |
577717 |
#define LOG_INFO(f, x...) printf(PFMSETUP_NAME ": " f "\n" , ## x)
|
|
Packit |
577717 |
|
|
Packit |
577717 |
typedef int (*command_fn)(int argc, char **argv);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
struct command {
|
|
Packit |
577717 |
const char *full_name;
|
|
Packit |
577717 |
const char *short_name;
|
|
Packit |
577717 |
const char *help;
|
|
Packit |
577717 |
command_fn fn;
|
|
Packit |
577717 |
int min_args;
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
struct context {
|
|
Packit |
577717 |
int id;
|
|
Packit |
577717 |
int fd;
|
|
Packit |
577717 |
int cpu;
|
|
Packit |
577717 |
pfarg_ctx_t ctx_arg;
|
|
Packit |
577717 |
pfm_dfl_smpl_arg_t smpl_arg;
|
|
Packit |
577717 |
struct event_set *event_sets;
|
|
Packit |
577717 |
struct context *next;
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
struct event_set {
|
|
Packit |
577717 |
int id;
|
|
Packit |
577717 |
struct event_set *next;
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
struct program {
|
|
Packit |
577717 |
int id;
|
|
Packit |
577717 |
pid_t pid;
|
|
Packit |
577717 |
struct program *next;
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Global list of all contexts that have been created. List is ordered by
|
|
Packit |
577717 |
* context id. Each context contains a list of event-sets belonging to that
|
|
Packit |
577717 |
* context, which is ordered by event-set id.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
static struct context *contexts = NULL;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Global list of all programs that have been started.
|
|
Packit |
577717 |
* List is ordered by program id.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
static struct program *programs = NULL;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* Routines to manipulate the context, event-set, and program lists.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static struct context *find_context(int ctx_id)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct context *ctx;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (ctx = contexts; ctx; ctx = ctx->next) {
|
|
Packit |
577717 |
if (ctx->id == ctx_id) {
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return ctx;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void insert_context(struct context *ctx)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct context **next_ctx;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (next_ctx = &contexts;
|
|
Packit |
577717 |
*next_ctx && (*next_ctx)->id < ctx->id;
|
|
Packit |
577717 |
next_ctx = &((*next_ctx)->next)) {
|
|
Packit |
577717 |
;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx->next = *next_ctx;
|
|
Packit |
577717 |
*next_ctx = ctx;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void remove_context(struct context *ctx)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct context **next_ctx;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (next_ctx = &contexts; *next_ctx; next_ctx = &((*next_ctx)->next)) {
|
|
Packit |
577717 |
if (*next_ctx == ctx) {
|
|
Packit |
577717 |
*next_ctx = ctx->next;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static struct event_set *find_event_set(struct context *ctx, int event_set_id)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct event_set *evt;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (evt = ctx->event_sets; evt; evt = evt->next) {
|
|
Packit |
577717 |
if (evt->id == event_set_id) {
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return evt;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void insert_event_set(struct context *ctx, struct event_set *evt)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct event_set **next_evt;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (next_evt = &ctx->event_sets;
|
|
Packit |
577717 |
*next_evt && (*next_evt)->id < evt->id;
|
|
Packit |
577717 |
next_evt = &((*next_evt)->next)) {
|
|
Packit |
577717 |
;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
evt->next = *next_evt;
|
|
Packit |
577717 |
*next_evt = evt;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void remove_event_set(struct context *ctx, struct event_set *evt)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct event_set **next_evt;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (next_evt = &ctx->event_sets;
|
|
Packit |
577717 |
*next_evt;
|
|
Packit |
577717 |
next_evt = &((*next_evt)->next)) {
|
|
Packit |
577717 |
if (*next_evt == evt) {
|
|
Packit |
577717 |
*next_evt = evt->next;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static struct program *find_program(int program_id)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct program *prog;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (prog = programs; prog; prog = prog->next) {
|
|
Packit |
577717 |
if (prog->id == program_id) {
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return prog;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void insert_program(struct program *prog)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct program **next_prog;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (next_prog = &programs;
|
|
Packit |
577717 |
*next_prog && (*next_prog)->id < prog->id;
|
|
Packit |
577717 |
next_prog = &((*next_prog)->next)) {
|
|
Packit |
577717 |
;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
prog->next = *next_prog;
|
|
Packit |
577717 |
*next_prog = prog;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void remove_program(struct program *prog)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct program **next_prog;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (next_prog = &programs;
|
|
Packit |
577717 |
*next_prog;
|
|
Packit |
577717 |
next_prog = &((*next_prog)->next)) {
|
|
Packit |
577717 |
if (*next_prog == prog) {
|
|
Packit |
577717 |
*next_prog = prog->next;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* set_affinity
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* When loading or unloading a system-wide context, we must pin the pfmsetup
|
|
Packit |
577717 |
* process to that CPU before making the system call. Also, get the current
|
|
Packit |
577717 |
* affinity and return it to the caller so we can change it back later.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static int set_affinity(int cpu, cpu_set_t *old_cpu_set)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
cpu_set_t new_cpu_set;
|
|
Packit |
577717 |
int rc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rc = sched_getaffinity(0, sizeof(*old_cpu_set), old_cpu_set);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
rc = errno;
|
|
Packit |
577717 |
LOG_ERROR("Can't get current process affinity mask: %d\n", rc);
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
CPU_ZERO(&new_cpu_set);
|
|
Packit |
577717 |
CPU_SET(cpu, &new_cpu_set);
|
|
Packit |
577717 |
rc = sched_setaffinity(0, sizeof(new_cpu_set), &new_cpu_set);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
rc = errno;
|
|
Packit |
577717 |
LOG_ERROR("Can't set process affinity to CPU %d: %d\n", cpu, rc);
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* revert_affinity
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Reset the process affinity to the specified mask.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static void revert_affinity(cpu_set_t *old_cpu_set)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int rc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rc = sched_setaffinity(0, sizeof(*old_cpu_set), old_cpu_set);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
/* Not a fatal error if we can't reset the affinity. */
|
|
Packit |
577717 |
LOG_INFO("Can't revert process affinity to original value.\n");
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* create_context
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Arguments: [options] <context_id>
|
|
Packit |
577717 |
* Options: --system
|
|
Packit |
577717 |
* --no-overflow-msg
|
|
Packit |
577717 |
* --block-on-notify
|
|
Packit |
577717 |
* --sampler <sampler_name>
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Call the pfm_create_context system-call to create a new perfmon context.
|
|
Packit |
577717 |
* Add a new entry to the global 'contexts' list.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static int create_context(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
pfarg_ctx_t ctx_arg;
|
|
Packit |
577717 |
pfm_dfl_smpl_arg_t smpl_arg;
|
|
Packit |
577717 |
struct context *new_ctx = NULL;
|
|
Packit |
577717 |
char *sampler_name = NULL;
|
|
Packit |
577717 |
void *smpl_p;
|
|
Packit |
577717 |
int no_overflow_msg = FALSE;
|
|
Packit |
577717 |
int block_on_notify = FALSE;
|
|
Packit |
577717 |
int system_wide = FALSE;
|
|
Packit |
577717 |
int c, ctx_id = 0;
|
|
Packit |
577717 |
int rc;
|
|
Packit |
577717 |
size_t sz;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
struct option long_opts[] = {
|
|
Packit |
577717 |
{"sampler", required_argument, NULL, 1},
|
|
Packit |
577717 |
{"system", no_argument, NULL, 2},
|
|
Packit |
577717 |
{"no-overflow-msg", no_argument, NULL, 3},
|
|
Packit |
577717 |
{"block-on-notify", no_argument, NULL, 4},
|
|
Packit |
577717 |
{NULL, 0, NULL, 0} };
|
|
Packit |
577717 |
|
|
Packit |
577717 |
memset(&ctx_arg, 0, sizeof(ctx_arg));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
opterr = 0;
|
|
Packit |
577717 |
optind = 0;
|
|
Packit |
577717 |
while ((c = getopt_long_only(argc, argv, "",
|
|
Packit |
577717 |
long_opts, NULL)) != EOF) {
|
|
Packit |
577717 |
switch (c) {
|
|
Packit |
577717 |
case 1:
|
|
Packit |
577717 |
sampler_name = optarg;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 2:
|
|
Packit |
577717 |
system_wide = TRUE;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 3:
|
|
Packit |
577717 |
no_overflow_msg = TRUE;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 4:
|
|
Packit |
577717 |
block_on_notify = TRUE;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
default:
|
|
Packit |
577717 |
LOG_ERROR("invalid option: %c", optopt);
|
|
Packit |
577717 |
rc = EINVAL;
|
|
Packit |
577717 |
goto error;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (argc < optind + 1) {
|
|
Packit |
577717 |
USAGE("create_context [options] <context_id>");
|
|
Packit |
577717 |
rc = EINVAL;
|
|
Packit |
577717 |
goto error;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx_id = strtoul(argv[optind], NULL, 0);
|
|
Packit |
577717 |
if (ctx_id <= 0) {
|
|
Packit |
577717 |
LOG_ERROR("Invalid context ID (%s). Must be a positive "
|
|
Packit |
577717 |
"integer.", argv[optind]);
|
|
Packit |
577717 |
rc = EINVAL;
|
|
Packit |
577717 |
goto error;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Make sure we don't already have a context with this ID. */
|
|
Packit |
577717 |
new_ctx = find_context(ctx_id);
|
|
Packit |
577717 |
if (new_ctx) {
|
|
Packit |
577717 |
LOG_ERROR("Context with ID %d already exists.", ctx_id);
|
|
Packit |
577717 |
rc = EINVAL;
|
|
Packit |
577717 |
goto error;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (sampler_name) {
|
|
Packit |
577717 |
smpl_arg.buf_size = getpagesize();
|
|
Packit |
577717 |
smpl_p = &smpl_arg;
|
|
Packit |
577717 |
sz = sizeof(smpl_arg);
|
|
Packit |
577717 |
} else {
|
|
Packit |
577717 |
smpl_p = NULL;
|
|
Packit |
577717 |
sz = 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx_arg.ctx_flags = (system_wide ? PFM_FL_SYSTEM_WIDE : 0) |
|
|
Packit |
577717 |
(no_overflow_msg ? PFM_FL_OVFL_NO_MSG : 0) |
|
|
Packit |
577717 |
(block_on_notify ? PFM_FL_NOTIFY_BLOCK : 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rc = pfm_create_context(&ctx_arg, sampler_name, smpl_p, sz);
|
|
Packit |
577717 |
if (rc == -1) {
|
|
Packit |
577717 |
rc = errno;
|
|
Packit |
577717 |
LOG_ERROR("pfm_create_context system call returned "
|
|
Packit |
577717 |
"an error: %d.", rc);
|
|
Packit |
577717 |
goto error;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Allocate and initialize a new context structure and add it to the
|
|
Packit |
577717 |
* global list. Every new context automatically gets one event_set
|
|
Packit |
577717 |
* with an event ID of 0.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
new_ctx = calloc(1, sizeof(*new_ctx));
|
|
Packit |
577717 |
if (!new_ctx) {
|
|
Packit |
577717 |
LOG_ERROR("Can't allocate structure for new context %d.",
|
|
Packit |
577717 |
ctx_id);
|
|
Packit |
577717 |
rc = ENOMEM;
|
|
Packit |
577717 |
goto error;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
new_ctx->event_sets = calloc(1, sizeof(*(new_ctx->event_sets)));
|
|
Packit |
577717 |
if (!new_ctx->event_sets) {
|
|
Packit |
577717 |
LOG_ERROR("Can't allocate event-set structure for new "
|
|
Packit |
577717 |
"context %d.", ctx_id);
|
|
Packit |
577717 |
rc = ENOMEM;
|
|
Packit |
577717 |
goto error;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
new_ctx->id = ctx_id;
|
|
Packit |
577717 |
new_ctx->fd = rc;
|
|
Packit |
577717 |
new_ctx->cpu = -1;
|
|
Packit |
577717 |
new_ctx->ctx_arg = ctx_arg;
|
|
Packit |
577717 |
new_ctx->smpl_arg = smpl_arg;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
insert_context(new_ctx);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
LOG_INFO("Created context %d with file-descriptor %d.",
|
|
Packit |
577717 |
new_ctx->id, new_ctx->fd);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
error:
|
|
Packit |
577717 |
if (new_ctx) {
|
|
Packit |
577717 |
close(new_ctx->fd);
|
|
Packit |
577717 |
free(new_ctx->event_sets);
|
|
Packit |
577717 |
free(new_ctx);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* load_context
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Arguments: <context_id> <event_set_id> <program_id|cpu_id>
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Call the pfm_load_context system-call to load a perfmon context into the
|
|
Packit |
577717 |
* system's performance monitoring unit.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static int load_context(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct context *ctx;
|
|
Packit |
577717 |
struct event_set *evt;
|
|
Packit |
577717 |
struct program *prog;
|
|
Packit |
577717 |
pfarg_load_t load_arg;
|
|
Packit |
577717 |
cpu_set_t old_cpu_set;
|
|
Packit |
577717 |
int ctx_id, event_set_id, program_id;
|
|
Packit |
577717 |
int system_wide, rc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx_id = strtoul(argv[1], NULL, 0);
|
|
Packit |
577717 |
event_set_id = strtoul(argv[2], NULL, 0);
|
|
Packit |
577717 |
program_id = strtoul(argv[3], NULL, 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (ctx_id <= 0 || event_set_id < 0 || program_id < 0) {
|
|
Packit |
577717 |
LOG_ERROR("context ID, event-set ID, and program/CPU ID must "
|
|
Packit |
577717 |
"be positive integers.");
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Find the context, event_set, and program in the global lists. */
|
|
Packit |
577717 |
ctx = find_context(ctx_id);
|
|
Packit |
577717 |
if (!ctx) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find context with ID %d.", ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
evt = find_event_set(ctx, event_set_id);
|
|
Packit |
577717 |
if (!evt) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find event-set with ID %d in context %d.",
|
|
Packit |
577717 |
event_set_id, ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
load_arg.load_set = evt->id;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
system_wide = ctx->ctx_arg.ctx_flags & PFM_FL_SYSTEM_WIDE;
|
|
Packit |
577717 |
if (system_wide) {
|
|
Packit |
577717 |
if (ctx->cpu >= 0) {
|
|
Packit |
577717 |
LOG_ERROR("Trying to load context %d which is already "
|
|
Packit |
577717 |
"loaded on CPU %d.\n", ctx_id, ctx->cpu);
|
|
Packit |
577717 |
return EBUSY;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rc = set_affinity(program_id, &old_cpu_set);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Specify the CPU as the PID. */
|
|
Packit |
577717 |
load_arg.load_pid = program_id;
|
|
Packit |
577717 |
} else {
|
|
Packit |
577717 |
prog = find_program(program_id);
|
|
Packit |
577717 |
if (!prog) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find program with ID %d.", program_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
load_arg.load_pid = prog->pid;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rc = pfm_load_context(ctx->fd, &load_arg);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
rc = errno;
|
|
Packit |
577717 |
LOG_ERROR("pfm_load_context system call returned "
|
|
Packit |
577717 |
"an error: %d.", rc);
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (system_wide) {
|
|
Packit |
577717 |
/* Keep track of which CPU this context is loaded on. */
|
|
Packit |
577717 |
ctx->cpu = program_id;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
revert_affinity(&old_cpu_set);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
LOG_INFO("Loaded context %d, event-set %d onto %s %d.",
|
|
Packit |
577717 |
ctx_id, event_set_id, system_wide ? "cpu" : "program",
|
|
Packit |
577717 |
program_id);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* unload_context
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Arguments: <context_id>
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Call the pfm_unload_context system-call to unload a perfmon context from
|
|
Packit |
577717 |
* the system's performance monitoring unit.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static int unload_context(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct context *ctx;
|
|
Packit |
577717 |
cpu_set_t old_cpu_set;
|
|
Packit |
577717 |
int system_wide;
|
|
Packit |
577717 |
int ctx_id;
|
|
Packit |
577717 |
int rc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx_id = strtoul(argv[1], NULL, 0);
|
|
Packit |
577717 |
if (ctx_id <= 0) {
|
|
Packit |
577717 |
LOG_ERROR("context ID must be a positive integer.");
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx = find_context(ctx_id);
|
|
Packit |
577717 |
if (!ctx) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find context with ID %d.", ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
system_wide = ctx->ctx_arg.ctx_flags & PFM_FL_SYSTEM_WIDE;
|
|
Packit |
577717 |
if (system_wide) {
|
|
Packit |
577717 |
if (ctx->cpu < 0) {
|
|
Packit |
577717 |
/* This context isn't loaded on any CPU. */
|
|
Packit |
577717 |
LOG_ERROR("Trying to unload context %d that isn't "
|
|
Packit |
577717 |
"loaded.\n", ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rc = set_affinity(ctx->cpu, &old_cpu_set);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rc = pfm_unload_context(ctx->fd);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
rc = errno;
|
|
Packit |
577717 |
LOG_ERROR("pfm_unload_context system call returned "
|
|
Packit |
577717 |
"an error: %d.", rc);
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (system_wide) {
|
|
Packit |
577717 |
ctx->cpu = -1;
|
|
Packit |
577717 |
revert_affinity(&old_cpu_set);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
LOG_INFO("Unloaded context %d.", ctx_id);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* close_context
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Arguments: <context_id>
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Close the context's file descriptor, remove it from the global list, and
|
|
Packit |
577717 |
* free the context data structures.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static int close_context(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct context *ctx;
|
|
Packit |
577717 |
struct event_set *evt, *next_evt;
|
|
Packit |
577717 |
int ctx_id;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx_id = strtoul(argv[1], NULL, 0);
|
|
Packit |
577717 |
if (ctx_id <= 0) {
|
|
Packit |
577717 |
LOG_ERROR("context ID must be a positive integer.");
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx = find_context(ctx_id);
|
|
Packit |
577717 |
if (!ctx) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find context with ID %d.", ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* There's no perfmon system-call to delete a context. We simply call
|
|
Packit |
577717 |
* close on the file handle.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
close(ctx->fd);
|
|
Packit |
577717 |
remove_context(ctx);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (evt = ctx->event_sets; evt; evt = next_evt) {
|
|
Packit |
577717 |
next_evt = evt->next;
|
|
Packit |
577717 |
free(evt);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
free(ctx);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
LOG_INFO("Closed and freed context %d.", ctx_id);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* write_pmc
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Arguments: <context_id> <event_set_id> <<pmc_id> <pmc_value>>+
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Write values to one or more control registers.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static int write_pmc(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct context *ctx;
|
|
Packit |
577717 |
struct event_set *evt;
|
|
Packit |
577717 |
pfarg_pmc_t *pmc_args = NULL;
|
|
Packit |
577717 |
cpu_set_t old_cpu_set;
|
|
Packit |
577717 |
int ctx_id, event_set_id;
|
|
Packit |
577717 |
int pmc_id, num_pmcs;
|
|
Packit |
577717 |
unsigned long long pmc_value;
|
|
Packit |
577717 |
int system_wide, i, rc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx_id = strtoul(argv[1], NULL, 0);
|
|
Packit |
577717 |
event_set_id = strtoul(argv[2], NULL, 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (ctx_id <= 0 || event_set_id < 0) {
|
|
Packit |
577717 |
LOG_ERROR("context ID and event-set ID must be "
|
|
Packit |
577717 |
"positive integers.");
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx = find_context(ctx_id);
|
|
Packit |
577717 |
if (!ctx) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find context with ID %d.", ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
evt = find_event_set(ctx, event_set_id);
|
|
Packit |
577717 |
if (!evt) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find event-set with ID %d in context %d.",
|
|
Packit |
577717 |
event_set_id, ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Allocate an array of PMC structures. */
|
|
Packit |
577717 |
num_pmcs = (argc - 3) / 2;
|
|
Packit |
577717 |
pmc_args = calloc(num_pmcs, sizeof(*pmc_args));
|
|
Packit |
577717 |
if (!pmc_args) {
|
|
Packit |
577717 |
LOG_ERROR("Can't allocate PMC argument array.");
|
|
Packit |
577717 |
return ENOMEM;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (i = 0; i < num_pmcs; i++) {
|
|
Packit |
577717 |
pmc_id = strtoul(argv[3 + i*2], NULL, 0);
|
|
Packit |
577717 |
pmc_value = strtoull(argv[4 + i*2], NULL, 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (pmc_id < 0) {
|
|
Packit |
577717 |
LOG_ERROR("PMC ID must be a positive integer.");
|
|
Packit |
577717 |
rc = EINVAL;
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pmc_args[i].reg_num = pmc_id;
|
|
Packit |
577717 |
pmc_args[i].reg_set = evt->id;
|
|
Packit |
577717 |
pmc_args[i].reg_value = pmc_value;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
system_wide = ctx->ctx_arg.ctx_flags & PFM_FL_SYSTEM_WIDE;
|
|
Packit |
577717 |
if (system_wide && ctx->cpu >= 0) {
|
|
Packit |
577717 |
rc = set_affinity(ctx->cpu, &old_cpu_set);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rc = pfm_write_pmcs(ctx->fd, pmc_args, num_pmcs);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
rc = errno;
|
|
Packit |
577717 |
LOG_ERROR("pfm_write_pmcs system call returned "
|
|
Packit |
577717 |
"an error: %d.", rc);
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (system_wide && ctx->cpu >= 0) {
|
|
Packit |
577717 |
revert_affinity(&old_cpu_set);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
out:
|
|
Packit |
577717 |
free(pmc_args);
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* write_pmd
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Arguments: <context_id> <event_set_id> <<pmd_id> <pmd_value>>+
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* FIXME: Add options for other fields in pfarg_pmd_t.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static int write_pmd(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct context *ctx;
|
|
Packit |
577717 |
struct event_set *evt;
|
|
Packit |
577717 |
pfarg_pmd_t *pmd_args = NULL;
|
|
Packit |
577717 |
cpu_set_t old_cpu_set;
|
|
Packit |
577717 |
int ctx_id, event_set_id;
|
|
Packit |
577717 |
int pmd_id, num_pmds;
|
|
Packit |
577717 |
unsigned long long pmd_value;
|
|
Packit |
577717 |
int system_wide, i, rc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx_id = strtoul(argv[1], NULL, 0);
|
|
Packit |
577717 |
event_set_id = strtoul(argv[2], NULL, 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (ctx_id <= 0 || event_set_id < 0) {
|
|
Packit |
577717 |
LOG_ERROR("context ID and event-set ID must be "
|
|
Packit |
577717 |
"positive integers.");
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx = find_context(ctx_id);
|
|
Packit |
577717 |
if (!ctx) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find context with ID %d.", ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
evt = find_event_set(ctx, event_set_id);
|
|
Packit |
577717 |
if (!evt) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find event-set with ID %d in context %d.",
|
|
Packit |
577717 |
event_set_id, ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Allocate an array of PMD structures. */
|
|
Packit |
577717 |
num_pmds = (argc - 3) / 2;
|
|
Packit |
577717 |
pmd_args = calloc(num_pmds, sizeof(*pmd_args));
|
|
Packit |
577717 |
if (!pmd_args) {
|
|
Packit |
577717 |
LOG_ERROR("Can't allocate PMD argument array.");
|
|
Packit |
577717 |
return ENOMEM;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (i = 0; i < num_pmds; i++) {
|
|
Packit |
577717 |
pmd_id = strtoul(argv[3 + i*2], NULL, 0);
|
|
Packit |
577717 |
pmd_value = strtoull(argv[4 + i*2], NULL, 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (pmd_id < 0) {
|
|
Packit |
577717 |
LOG_ERROR("PMD ID must be a positive integer.");
|
|
Packit |
577717 |
rc = EINVAL;
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pmd_args[i].reg_num = pmd_id;
|
|
Packit |
577717 |
pmd_args[i].reg_set = evt->id;
|
|
Packit |
577717 |
pmd_args[i].reg_value = pmd_value;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
system_wide = ctx->ctx_arg.ctx_flags & PFM_FL_SYSTEM_WIDE;
|
|
Packit |
577717 |
if (system_wide && ctx->cpu >= 0) {
|
|
Packit |
577717 |
rc = set_affinity(ctx->cpu, &old_cpu_set);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rc = pfm_write_pmds(ctx->fd, pmd_args, num_pmds);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
rc = errno;
|
|
Packit |
577717 |
LOG_ERROR("pfm_write_pmds system call returned "
|
|
Packit |
577717 |
"an error: %d.", rc);
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (system_wide && ctx->cpu >= 0) {
|
|
Packit |
577717 |
revert_affinity(&old_cpu_set);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
out:
|
|
Packit |
577717 |
free(pmd_args);
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* read_pmd
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Arguments: <context_id> <event_set_id> <pmd_id>+
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* FIXME: Add options for other fields in pfarg_pmd_t.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static int read_pmd(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct context *ctx;
|
|
Packit |
577717 |
struct event_set *evt;
|
|
Packit |
577717 |
pfarg_pmd_t *pmd_args = NULL;
|
|
Packit |
577717 |
cpu_set_t old_cpu_set;
|
|
Packit |
577717 |
int ctx_id, event_set_id;
|
|
Packit |
577717 |
int pmd_id, num_pmds;
|
|
Packit |
577717 |
int system_wide, i, rc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx_id = strtoul(argv[1], NULL, 0);
|
|
Packit |
577717 |
event_set_id = strtoul(argv[2], NULL, 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (ctx_id <= 0 || event_set_id < 0) {
|
|
Packit |
577717 |
LOG_ERROR("context ID and event-set ID must be "
|
|
Packit |
577717 |
"positive integers.");
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx = find_context(ctx_id);
|
|
Packit |
577717 |
if (!ctx) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find context with ID %d.", ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
evt = find_event_set(ctx, event_set_id);
|
|
Packit |
577717 |
if (!evt) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find event-set with ID %d in context %d.",
|
|
Packit |
577717 |
event_set_id, ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Allocate an array of PMD structures. */
|
|
Packit |
577717 |
num_pmds = argc - 3;
|
|
Packit |
577717 |
pmd_args = calloc(num_pmds, sizeof(*pmd_args));
|
|
Packit |
577717 |
if (!pmd_args) {
|
|
Packit |
577717 |
LOG_ERROR("Can't allocate PMD argument array.");
|
|
Packit |
577717 |
return ENOMEM;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (i = 0; i < num_pmds; i++) {
|
|
Packit |
577717 |
pmd_id = strtoul(argv[3 + i], NULL, 0);
|
|
Packit |
577717 |
if (pmd_id < 0) {
|
|
Packit |
577717 |
LOG_ERROR("PMD ID must be a positive integer.");
|
|
Packit |
577717 |
rc = EINVAL;
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pmd_args[i].reg_num = pmd_id;
|
|
Packit |
577717 |
pmd_args[i].reg_set = evt->id;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
system_wide = ctx->ctx_arg.ctx_flags & PFM_FL_SYSTEM_WIDE;
|
|
Packit |
577717 |
if (system_wide && ctx->cpu >= 0) {
|
|
Packit |
577717 |
rc = set_affinity(ctx->cpu, &old_cpu_set);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rc = pfm_read_pmds(ctx->fd, pmd_args, num_pmds);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
rc = errno;
|
|
Packit |
577717 |
LOG_ERROR("pfm_read_pmds system call returned "
|
|
Packit |
577717 |
"an error: %d.", rc);
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (system_wide && ctx->cpu >= 0) {
|
|
Packit |
577717 |
revert_affinity(&old_cpu_set);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
out:
|
|
Packit |
577717 |
free(pmd_args);
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* start_counting
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Arguments: <context_id> <event_set_id>
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Call the pfm_start system-call to start counting for a perfmon context
|
|
Packit |
577717 |
* that was previously stopped.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static int start_counting(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
pfarg_start_t start_arg;
|
|
Packit |
577717 |
struct context *ctx;
|
|
Packit |
577717 |
struct event_set *evt;
|
|
Packit |
577717 |
cpu_set_t old_cpu_set;
|
|
Packit |
577717 |
int ctx_id, event_set_id;
|
|
Packit |
577717 |
int system_wide, rc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
memset(&start_arg, 0, sizeof(start_arg));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx_id = strtoul(argv[1], NULL, 0);
|
|
Packit |
577717 |
event_set_id = strtoul(argv[2], NULL, 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (ctx_id <= 0 || event_set_id < 0) {
|
|
Packit |
577717 |
LOG_ERROR("context ID and event-set ID must be "
|
|
Packit |
577717 |
"positive integers.");
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx = find_context(ctx_id);
|
|
Packit |
577717 |
if (!ctx) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find context with ID %d.", ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
evt = find_event_set(ctx, event_set_id);
|
|
Packit |
577717 |
if (!evt) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find event-set with ID %d in context %d.",
|
|
Packit |
577717 |
event_set_id, ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
start_arg.start_set = evt->id;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
system_wide = ctx->ctx_arg.ctx_flags & PFM_FL_SYSTEM_WIDE;
|
|
Packit |
577717 |
if (system_wide && ctx->cpu >= 0) {
|
|
Packit |
577717 |
rc = set_affinity(ctx->cpu, &old_cpu_set);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rc = pfm_start(ctx->fd, &start_arg);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
rc = errno;
|
|
Packit |
577717 |
LOG_ERROR("pfm_start system call returned an error: %d.", rc);
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (system_wide && ctx->cpu >= 0) {
|
|
Packit |
577717 |
revert_affinity(&old_cpu_set);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
LOG_INFO("Started counting for context %d, event-set %d.",
|
|
Packit |
577717 |
ctx_id, event_set_id);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* stop_counting
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Arguments: <context_id>
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Call the pfm_stop system-call to stop counting for a perfmon context that
|
|
Packit |
577717 |
* was previously loaded.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static int stop_counting(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct context *ctx;
|
|
Packit |
577717 |
cpu_set_t old_cpu_set;
|
|
Packit |
577717 |
int system_wide;
|
|
Packit |
577717 |
int ctx_id;
|
|
Packit |
577717 |
int rc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx_id = strtoul(argv[1], NULL, 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (ctx_id <= 0) {
|
|
Packit |
577717 |
LOG_ERROR("context ID must be a positive integer.");
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx = find_context(ctx_id);
|
|
Packit |
577717 |
if (!ctx) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find context with ID %d.", ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
system_wide = ctx->ctx_arg.ctx_flags & PFM_FL_SYSTEM_WIDE;
|
|
Packit |
577717 |
if (system_wide && ctx->cpu >= 0) {
|
|
Packit |
577717 |
rc = set_affinity(ctx->cpu, &old_cpu_set);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rc = pfm_stop(ctx->fd);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
rc = errno;
|
|
Packit |
577717 |
LOG_ERROR("pfm_stop system call returned an error: %d.", rc);
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (system_wide && ctx->cpu >= 0) {
|
|
Packit |
577717 |
revert_affinity(&old_cpu_set);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
LOG_INFO("Stopped counting for context %d.", ctx_id);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* restart_counting
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Arguments: <context_id>
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Call the pfm_restart system-call to clear the data counters and start
|
|
Packit |
577717 |
* counting from zero for a perfmon context that was previously loaded.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static int restart_counting(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct context *ctx;
|
|
Packit |
577717 |
cpu_set_t old_cpu_set;
|
|
Packit |
577717 |
int system_wide;
|
|
Packit |
577717 |
int ctx_id;
|
|
Packit |
577717 |
int rc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx_id = strtoul(argv[1], NULL, 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (ctx_id <= 0) {
|
|
Packit |
577717 |
LOG_ERROR("context ID must be a positive integer.");
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx = find_context(ctx_id);
|
|
Packit |
577717 |
if (!ctx) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find context with ID %d.", ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
system_wide = ctx->ctx_arg.ctx_flags & PFM_FL_SYSTEM_WIDE;
|
|
Packit |
577717 |
if (system_wide && ctx->cpu >= 0) {
|
|
Packit |
577717 |
rc = set_affinity(ctx->cpu, &old_cpu_set);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rc = pfm_restart(ctx->fd);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
rc = errno;
|
|
Packit |
577717 |
LOG_ERROR("pfm_restart system call returned an error: %d.", rc);
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (system_wide && ctx->cpu >= 0) {
|
|
Packit |
577717 |
revert_affinity(&old_cpu_set);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
LOG_INFO("Restarted counting for context %d.", ctx_id);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* create_eventset
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Arguments: [options] <context_id> <event_set_id>
|
|
Packit |
577717 |
* Options: --timeout <nanoseconds>
|
|
Packit |
577717 |
* --switch-on-overflow
|
|
Packit |
577717 |
* --exclude-idle
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static int create_eventset(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
pfarg_setdesc_t set_arg;
|
|
Packit |
577717 |
struct context *ctx;
|
|
Packit |
577717 |
struct event_set *evt;
|
|
Packit |
577717 |
cpu_set_t old_cpu_set;
|
|
Packit |
577717 |
int ctx_id, event_set_id;
|
|
Packit |
577717 |
unsigned long timeout = 0;
|
|
Packit |
577717 |
int switch_on_overflow = FALSE;
|
|
Packit |
577717 |
int switch_on_timeout = FALSE;
|
|
Packit |
577717 |
int exclude_idle = FALSE;
|
|
Packit |
577717 |
int new_set = FALSE;
|
|
Packit |
577717 |
int system_wide,c, rc;
|
|
Packit |
577717 |
struct option long_opts[] = {
|
|
Packit |
577717 |
{"next-set", required_argument, NULL, 1},
|
|
Packit |
577717 |
{"timeout", required_argument, NULL, 2},
|
|
Packit |
577717 |
{"switch-on-overflow", no_argument, NULL, 3},
|
|
Packit |
577717 |
{"exclude-idle", no_argument, NULL, 4},
|
|
Packit |
577717 |
{NULL, 0, NULL, 0} };
|
|
Packit |
577717 |
|
|
Packit |
577717 |
memset(&set_arg, 0, sizeof(set_arg));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
opterr = 0;
|
|
Packit |
577717 |
optind = 0;
|
|
Packit |
577717 |
while ((c = getopt_long_only(argc, argv, "",
|
|
Packit |
577717 |
long_opts, NULL)) != EOF) {
|
|
Packit |
577717 |
switch (c) {
|
|
Packit |
577717 |
case 1:
|
|
Packit |
577717 |
timeout = strtoul(optarg, NULL, 0);
|
|
Packit |
577717 |
if (!timeout) {
|
|
Packit |
577717 |
LOG_ERROR("timeout must be a "
|
|
Packit |
577717 |
"non-zero integer.");
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
switch_on_timeout = TRUE;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 2:
|
|
Packit |
577717 |
switch_on_overflow = TRUE;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 3:
|
|
Packit |
577717 |
exclude_idle = TRUE;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
default:
|
|
Packit |
577717 |
LOG_ERROR("invalid option: %c", optopt);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
(void) exclude_idle;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (argc < optind + 2) {
|
|
Packit |
577717 |
USAGE("create_eventset [options] <context_id> <event_set_id>");
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx_id = strtoul(argv[optind], NULL, 0);
|
|
Packit |
577717 |
event_set_id = strtoul(argv[optind+1], NULL, 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (ctx_id <= 0 || event_set_id < 0) {
|
|
Packit |
577717 |
LOG_ERROR("context ID and event-set ID must be "
|
|
Packit |
577717 |
"positive integers.");
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx = find_context(ctx_id);
|
|
Packit |
577717 |
if (!ctx) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find context with ID %d.", ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (switch_on_timeout && switch_on_overflow) {
|
|
Packit |
577717 |
LOG_ERROR("Cannot switch set %d (context %d) on both "
|
|
Packit |
577717 |
"timeout and overflow.", event_set_id, ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
evt = find_event_set(ctx, event_set_id);
|
|
Packit |
577717 |
if (!evt) {
|
|
Packit |
577717 |
evt = calloc(1, sizeof(*evt));
|
|
Packit |
577717 |
if (!evt) {
|
|
Packit |
577717 |
LOG_ERROR("Can't allocate structure for new event-set "
|
|
Packit |
577717 |
"%d in context %d.", event_set_id, ctx_id);
|
|
Packit |
577717 |
return ENOMEM;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
evt->id = event_set_id;
|
|
Packit |
577717 |
new_set = TRUE;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
set_arg.set_id = event_set_id;
|
|
Packit |
577717 |
set_arg.set_timeout = timeout; /* in nanseconds */
|
|
Packit |
577717 |
set_arg.set_flags = (switch_on_overflow ? PFM_SETFL_OVFL_SWITCH : 0) |
|
|
Packit |
577717 |
(switch_on_timeout ? PFM_SETFL_TIME_SWITCH : 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
system_wide = ctx->ctx_arg.ctx_flags & PFM_FL_SYSTEM_WIDE;
|
|
Packit |
577717 |
if (system_wide && ctx->cpu >= 0) {
|
|
Packit |
577717 |
rc = set_affinity(ctx->cpu, &old_cpu_set);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
free(evt);
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rc = pfm_create_evtsets(ctx->fd, &set_arg, 1);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
rc = errno;
|
|
Packit |
577717 |
LOG_ERROR("pfm_create_evtsets system call returned "
|
|
Packit |
577717 |
"an error: %d.", rc);
|
|
Packit |
577717 |
free(evt);
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (system_wide && ctx->cpu >= 0) {
|
|
Packit |
577717 |
revert_affinity(&old_cpu_set);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (new_set) {
|
|
Packit |
577717 |
insert_event_set(ctx, evt);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
LOG_INFO("%s event-set %d in context %d.",
|
|
Packit |
577717 |
new_set ? "Created" : "Modified", event_set_id, ctx_id);
|
|
Packit |
577717 |
if (switch_on_timeout) {
|
|
Packit |
577717 |
LOG_INFO(" Actual timeout set to %llu ns.",
|
|
Packit |
577717 |
(unsigned long long)set_arg.set_timeout);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* delete_eventset
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Arguments: <context_id> <event_set_id>
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static int delete_eventset(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
pfarg_setdesc_t set_arg;
|
|
Packit |
577717 |
struct context *ctx;
|
|
Packit |
577717 |
struct event_set *evt;
|
|
Packit |
577717 |
cpu_set_t old_cpu_set;
|
|
Packit |
577717 |
int ctx_id, event_set_id;
|
|
Packit |
577717 |
int system_wide, rc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
memset(&set_arg, 0, sizeof(set_arg));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx_id = strtoul(argv[1], NULL, 0);
|
|
Packit |
577717 |
event_set_id = strtoul(argv[2], NULL, 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (ctx_id <= 0 || event_set_id < 0) {
|
|
Packit |
577717 |
LOG_ERROR("context ID and event-set ID must be "
|
|
Packit |
577717 |
"positive integers.");
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx = find_context(ctx_id);
|
|
Packit |
577717 |
if (!ctx) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find context with ID %d.", ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
evt = find_event_set(ctx, event_set_id);
|
|
Packit |
577717 |
if (!evt) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find event-set with ID %d in context %d.",
|
|
Packit |
577717 |
event_set_id, ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
set_arg.set_id = evt->id;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
system_wide = ctx->ctx_arg.ctx_flags & PFM_FL_SYSTEM_WIDE;
|
|
Packit |
577717 |
if (system_wide && ctx->cpu >= 0) {
|
|
Packit |
577717 |
rc = set_affinity(ctx->cpu, &old_cpu_set);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rc = pfm_delete_evtsets(ctx->fd, &set_arg, 1);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
rc = errno;
|
|
Packit |
577717 |
LOG_ERROR("pfm_delete_evtsets system call returned "
|
|
Packit |
577717 |
"an error: %d.", rc);
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (system_wide && ctx->cpu >= 0) {
|
|
Packit |
577717 |
revert_affinity(&old_cpu_set);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
remove_event_set(ctx, evt);
|
|
Packit |
577717 |
free(evt);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
LOG_INFO("Deleted event-set %d from context %d.", event_set_id, ctx_id);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* getinfo_eventset
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Arguments: <context_id> <event_set_id>
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static int getinfo_eventset(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
pfarg_setinfo_t set_arg;
|
|
Packit |
577717 |
struct context *ctx;
|
|
Packit |
577717 |
struct event_set *evt;
|
|
Packit |
577717 |
cpu_set_t old_cpu_set;
|
|
Packit |
577717 |
int ctx_id, event_set_id;
|
|
Packit |
577717 |
int system_wide, rc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
memset(&set_arg, 0, sizeof(set_arg));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx_id = strtoul(argv[1], NULL, 0);
|
|
Packit |
577717 |
event_set_id = strtoul(argv[2], NULL, 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (ctx_id <= 0 || event_set_id < 0) {
|
|
Packit |
577717 |
LOG_ERROR("context ID and event-set ID must be "
|
|
Packit |
577717 |
"positive integers.");
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ctx = find_context(ctx_id);
|
|
Packit |
577717 |
if (!ctx) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find context with ID %d.", ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
evt = find_event_set(ctx, event_set_id);
|
|
Packit |
577717 |
if (!evt) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find event-set with ID %d in context %d.",
|
|
Packit |
577717 |
event_set_id, ctx_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
set_arg.set_id = evt->id;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
system_wide = ctx->ctx_arg.ctx_flags & PFM_FL_SYSTEM_WIDE;
|
|
Packit |
577717 |
if (system_wide && ctx->cpu >= 0) {
|
|
Packit |
577717 |
rc = set_affinity(ctx->cpu, &old_cpu_set);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rc = pfm_getinfo_evtsets(ctx->fd, &set_arg, 1);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
rc = errno;
|
|
Packit |
577717 |
LOG_ERROR("pfm_getinfo_evtsets system call returned "
|
|
Packit |
577717 |
"an error: %d.", rc);
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (system_wide && ctx->cpu >= 0) {
|
|
Packit |
577717 |
revert_affinity(&old_cpu_set);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
LOG_INFO("Got info for event-set %d in context %d.", event_set_id, ctx_id);
|
|
Packit |
577717 |
LOG_INFO(" Flags: 0x%x", set_arg.set_flags);
|
|
Packit |
577717 |
LOG_INFO(" Runs: %llu", (unsigned long long)set_arg.set_runs);
|
|
Packit |
577717 |
LOG_INFO(" Timeout: %"PRIu64, set_arg.set_timeout);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* run_program
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Arguments: <program_id> <program name and arguments>
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Start the specified program. After fork'ing but before exec'ing, ptrace
|
|
Packit |
577717 |
* the child so it will remain suspended until a corresponding resume_program
|
|
Packit |
577717 |
* command. We do this so we can load a context for the program before it
|
|
Packit |
577717 |
* actually starts running. This logic is taken from the task.c example in
|
|
Packit |
577717 |
* the libpfm source code tree.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static int run_program(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct program *prog;
|
|
Packit |
577717 |
int program_id;
|
|
Packit |
577717 |
pid_t pid;
|
|
Packit |
577717 |
int rc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
program_id = strtoul(argv[1], NULL, 0);
|
|
Packit |
577717 |
if (program_id <= 0) {
|
|
Packit |
577717 |
LOG_ERROR("program ID must be a positive integer.");
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Make sure we haven't already started a program with this ID. */
|
|
Packit |
577717 |
prog = find_program(program_id);
|
|
Packit |
577717 |
if (prog) {
|
|
Packit |
577717 |
LOG_ERROR("Program with ID %d already exists.", program_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
prog = calloc(1, sizeof(*prog));
|
|
Packit |
577717 |
if (!prog) {
|
|
Packit |
577717 |
LOG_ERROR("Can't allocate new program structure to run '%s'.",
|
|
Packit |
577717 |
argv[2]);
|
|
Packit |
577717 |
return ENOMEM;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
prog->id = program_id;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pid = fork();
|
|
Packit |
577717 |
if (pid == -1) {
|
|
Packit |
577717 |
/* Error fork'ing. */
|
|
Packit |
577717 |
LOG_ERROR("Unable to fork child process.");
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
} else if (!pid) {
|
|
Packit |
577717 |
/* Child */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* This will cause the program to stop before executing the
|
|
Packit |
577717 |
* first user level instruction. We can only load a context
|
|
Packit |
577717 |
* if the program is in the STOPPED state. This child
|
|
Packit |
577717 |
* process will sit here until we've process a resume_program
|
|
Packit |
577717 |
* command.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
rc = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
rc = errno;
|
|
Packit |
577717 |
LOG_ERROR("Error ptrace'ing '%s': %d", argv[2], rc);
|
|
Packit |
577717 |
exit(rc);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
execvp(argv[2], argv + 2);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rc = errno;
|
|
Packit |
577717 |
LOG_ERROR("Error exec'ing '%s': %d", argv[2], rc);
|
|
Packit |
577717 |
exit(rc);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Parent */
|
|
Packit |
577717 |
prog->pid = pid;
|
|
Packit |
577717 |
insert_program(prog);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Wait for the child to exec. */
|
|
Packit |
577717 |
waitpid(pid, &rc, WUNTRACED);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Check if process exited early. */
|
|
Packit |
577717 |
if (WIFEXITED(rc)) {
|
|
Packit |
577717 |
LOG_ERROR("Program '%s' exited too early with status "
|
|
Packit |
577717 |
"%d", argv[2], WEXITSTATUS(rc));
|
|
Packit |
577717 |
return WEXITSTATUS(rc);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
LOG_INFO("Started program %d: '%s'.", program_id, argv[2]);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* resume_program
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Arguments: <program_id>
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* A program started with run_program must be 'resumed' before it actually
|
|
Packit |
577717 |
* begins running. This allows us to load a context to the process and
|
|
Packit |
577717 |
* start the counters before the program executes any code.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static int resume_program(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct program *prog;
|
|
Packit |
577717 |
int program_id;
|
|
Packit |
577717 |
int rc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
program_id = strtoul(argv[1], NULL, 0);
|
|
Packit |
577717 |
if (program_id <= 0) {
|
|
Packit |
577717 |
LOG_ERROR("program ID must be a positive integer.");
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
prog = find_program(program_id);
|
|
Packit |
577717 |
if (!prog) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find program with ID %d.", program_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Call ptrace to resume execution of the process. If a context has
|
|
Packit |
577717 |
* been loaded and the counters started, this is where monitoring
|
|
Packit |
577717 |
* is effectively activated.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
rc = ptrace(PTRACE_DETACH, prog->pid, NULL, 0);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
rc = errno;
|
|
Packit |
577717 |
LOG_ERROR("Error detaching program %d.\n", prog->id);
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
LOG_INFO("Resumed program %d.", program_id);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* wait_on_program
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Arguments: <program_id>
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Wait for the specified program to complete and exit.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static int wait_on_program(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct program *prog;
|
|
Packit |
577717 |
int program_id;
|
|
Packit |
577717 |
int rc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
program_id = strtoul(argv[1], NULL, 0);
|
|
Packit |
577717 |
if (program_id <= 0) {
|
|
Packit |
577717 |
LOG_ERROR("program ID must be a positive integer.");
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
prog = find_program(program_id);
|
|
Packit |
577717 |
if (!prog) {
|
|
Packit |
577717 |
LOG_ERROR("Can't find program with ID %d.", program_id);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
waitpid(prog->pid, &rc, 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* The program has exitted, but if there was a context loaded on that
|
|
Packit |
577717 |
* process, it will still have the latest counts available to read.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
remove_program(prog);
|
|
Packit |
577717 |
free(prog);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
LOG_INFO("Waited for program %d to complete.", program_id);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* _sleep
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Arguments: <time in seconds>
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Wait for the specified number of seconds.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static int _sleep(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int seconds;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
seconds = strtoul(argv[1], NULL, 0);
|
|
Packit |
577717 |
if (seconds < 0) {
|
|
Packit |
577717 |
LOG_ERROR("time in seconds must be a positive integer.");
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
LOG_INFO("Sleeping for %d seconds.", seconds);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
while (seconds > 0)
|
|
Packit |
577717 |
seconds = sleep(seconds);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
LOG_INFO("Done sleeping.");
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* _commands
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Array to describe all the available commands, their options, and the
|
|
Packit |
577717 |
* routines that will process the commands.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* The concept for this array and the code to search it comes from the dmsetup
|
|
Packit |
577717 |
* program in the device-mapper project.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static struct command _commands[] = {
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{ "create_context", "cc",
|
|
Packit |
577717 |
"<context_id> [--system] [--no-overflow-msg] "
|
|
Packit |
577717 |
"[--block-on-notify] [--sampler <sampler_name>]",
|
|
Packit |
577717 |
create_context, 1 },
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{ "load_context", "load",
|
|
Packit |
577717 |
"<context_id> <event_set_id> <program_id|cpu_id>",
|
|
Packit |
577717 |
load_context, 3 },
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{ "unload_context", "unload",
|
|
Packit |
577717 |
"<context_id>",
|
|
Packit |
577717 |
unload_context, 1 },
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{ "close_context", "close",
|
|
Packit |
577717 |
"<context_id>",
|
|
Packit |
577717 |
close_context, 1 },
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{ "write_pmc", "wpmc",
|
|
Packit |
577717 |
"<context_id> <event_set_id> <<pmc_id> <pmc_value>>+",
|
|
Packit |
577717 |
write_pmc, 4 },
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{ "write_pmd", "wpmd",
|
|
Packit |
577717 |
"<context_id> <event_set_id> <<pmd_id> <pmd_value>>+",
|
|
Packit |
577717 |
write_pmd, 4 },
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{ "read_pmd", "rpmd",
|
|
Packit |
577717 |
"<context_id> <event_set_id> <pmd_id>+",
|
|
Packit |
577717 |
read_pmd, 3 },
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{ "start_counting", "start",
|
|
Packit |
577717 |
"<context_id> <event_set_id>",
|
|
Packit |
577717 |
start_counting, 2 },
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{ "stop_counting", "stop",
|
|
Packit |
577717 |
"<context_id>",
|
|
Packit |
577717 |
stop_counting, 1 },
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{ "restart_counting", "restart",
|
|
Packit |
577717 |
"<context_id>",
|
|
Packit |
577717 |
restart_counting, 1 },
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{ "create_eventset", "ce",
|
|
Packit |
577717 |
"<context_id> <event_set_id> [--next-set <next_event_set_id>] "
|
|
Packit |
577717 |
"[--timeout <nanoseconds>] [--switch-on-overflow] [--exclude-idle]",
|
|
Packit |
577717 |
create_eventset, 2 },
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{ "delete_eventset", "de",
|
|
Packit |
577717 |
"<context_id> <event_set_id>",
|
|
Packit |
577717 |
delete_eventset, 2 },
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{ "getinfo_eventset", "ge",
|
|
Packit |
577717 |
"<context_id> <event_set_id>",
|
|
Packit |
577717 |
getinfo_eventset, 2 },
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{ "run_program", "run",
|
|
Packit |
577717 |
"<program_id> <program command line and arguments>",
|
|
Packit |
577717 |
run_program, 2 },
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{ "resume_program", "resume",
|
|
Packit |
577717 |
"<program_id>",
|
|
Packit |
577717 |
resume_program, 1 },
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{ "wait_on_program", "wait",
|
|
Packit |
577717 |
"<program_id>",
|
|
Packit |
577717 |
wait_on_program, 1 },
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{ "sleep", "sleep",
|
|
Packit |
577717 |
"<time in seconds>",
|
|
Packit |
577717 |
_sleep, 1 },
|
|
Packit |
577717 |
|
|
Packit |
577717 |
{NULL, NULL, NULL, NULL, 0},
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* find_command
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Search for the specified command in the _commands array. The command
|
|
Packit |
577717 |
* can be specified using the full name or the short name.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static struct command *find_command(const char *command)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int i;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (i = 0; _commands[i].full_name; i++) {
|
|
Packit |
577717 |
if (!strcasecmp(command, _commands[i].full_name) ||
|
|
Packit |
577717 |
!strcasecmp(command, _commands[i].short_name)) {
|
|
Packit |
577717 |
return _commands + i;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return NULL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void print_help(const char *prog_name)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int i;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
LOG_INFO("USAGE: %s <command_file>", prog_name);
|
|
Packit |
577717 |
LOG_INFO("");
|
|
Packit |
577717 |
LOG_INFO("Available commands and arguments for command-file:");
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (i = 0; _commands[i].full_name; i++) {
|
|
Packit |
577717 |
LOG_INFO("\t%s (%s)", _commands[i].full_name,
|
|
Packit |
577717 |
_commands[i].short_name);
|
|
Packit |
577717 |
LOG_INFO("\t\t%s", _commands[i].help);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* free_lines
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Free all the strings that were read from the command file.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
void free_lines(char **lines)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int i;
|
|
Packit |
577717 |
if (lines) {
|
|
Packit |
577717 |
for (i = 0; lines[i]; i++) {
|
|
Packit |
577717 |
free(lines[i]);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
free(lines);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* read_file
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Read in the command-file. Create an array of strings, with one string
|
|
Packit |
577717 |
* for each line in the file. The last entry in the array will be NULL
|
|
Packit |
577717 |
* to indicate the end of the file.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static int read_file(FILE *fp, char ***lines)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
char one_line[1024], *str, **strings = NULL;
|
|
Packit |
577717 |
int num_lines = 1;
|
|
Packit |
577717 |
int i = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
while (1) {
|
|
Packit |
577717 |
str = fgets(one_line, 1024, fp);
|
|
Packit |
577717 |
if (!str) {
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (i == num_lines || !strings) {
|
|
Packit |
577717 |
num_lines *= 2;
|
|
Packit |
577717 |
strings = realloc(strings, num_lines * sizeof(*strings));
|
|
Packit |
577717 |
if (!strings) {
|
|
Packit |
577717 |
return ENOMEM;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
strings[i] = strdup(one_line);
|
|
Packit |
577717 |
if (!strings[i]) {
|
|
Packit |
577717 |
free_lines(strings);
|
|
Packit |
577717 |
return ENOMEM;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
i++;
|
|
Packit |
577717 |
strings[i] = NULL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
*lines = strings;
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/**
|
|
Packit |
577717 |
* tokenize
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Break up the specified line into whitespace-seperated tokens. Fill in
|
|
Packit |
577717 |
* the 'tokens' array with pointers to each token.
|
|
Packit |
577717 |
**/
|
|
Packit |
577717 |
static void tokenize(char *line, int *num_tokens, char **tokens)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
char *saved_line, *token;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
*num_tokens = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
while (1) {
|
|
Packit |
577717 |
token = strtok_r(line, WHITESPACE, &saved_line);
|
|
Packit |
577717 |
if (!token) {
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
tokens[*num_tokens] = token;
|
|
Packit |
577717 |
(*num_tokens)++;
|
|
Packit |
577717 |
if (*num_tokens >= MAX_TOKENS) {
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
line = NULL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
tokens[*num_tokens] = NULL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int main(int argc, char **argv)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
FILE *fp;
|
|
Packit |
577717 |
struct command *cmd;
|
|
Packit |
577717 |
char *filename;
|
|
Packit |
577717 |
char **lines;
|
|
Packit |
577717 |
char *tokens[MAX_TOKENS + 1] = {NULL};
|
|
Packit |
577717 |
int num_tokens;
|
|
Packit |
577717 |
int rc, i;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (argc < 2 ||
|
|
Packit |
577717 |
!strcmp(argv[1], "-?") ||
|
|
Packit |
577717 |
!strcasecmp(argv[1], "-h") ||
|
|
Packit |
577717 |
!strcasecmp(argv[1], "--help")) {
|
|
Packit |
577717 |
print_help(argv[0]);
|
|
Packit |
577717 |
return EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
filename = argv[1];
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Open the command file and read the entire
|
|
Packit |
577717 |
* contents into the 'lines' array.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
fp = fopen(filename, "r");
|
|
Packit |
577717 |
if (!fp) {
|
|
Packit |
577717 |
rc = errno;
|
|
Packit |
577717 |
LOG_ERROR("Can't open file %s.\n", filename);
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rc = read_file(fp, &lines);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
LOG_ERROR("Can't read file %s.\n", filename);
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (!lines) {
|
|
Packit |
577717 |
LOG_ERROR("File %s is empty.\n", filename);
|
|
Packit |
577717 |
rc = EINVAL;
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Process each line from the command file. */
|
|
Packit |
577717 |
for (i = 0; lines[i]; i++) {
|
|
Packit |
577717 |
tokenize(lines[i], &num_tokens, tokens);
|
|
Packit |
577717 |
if (!num_tokens) {
|
|
Packit |
577717 |
/* Skip empty lines. */
|
|
Packit |
577717 |
continue;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (tokens[0][0] == '#') {
|
|
Packit |
577717 |
/* Skip lines that start with '#'. */
|
|
Packit |
577717 |
continue;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* The first token specifies the command to run. Find this
|
|
Packit |
577717 |
* command in the array, check that we have enough arguments,
|
|
Packit |
577717 |
* and then run the command. If anything goes wrong with a
|
|
Packit |
577717 |
* command, we skip all remaining commands.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
cmd = find_command(tokens[0]);
|
|
Packit |
577717 |
if (!cmd) {
|
|
Packit |
577717 |
LOG_ERROR("Invalid command '%s' (line %d).\n",
|
|
Packit |
577717 |
tokens[0], i+1);
|
|
Packit |
577717 |
rc = EINVAL;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (num_tokens - 1 < cmd->min_args) {
|
|
Packit |
577717 |
LOG_ERROR("Incorrect number of arguments for command "
|
|
Packit |
577717 |
"\'%s\' (line %d)", tokens[0], i+1);
|
|
Packit |
577717 |
USAGE("%s %s", cmd->full_name, cmd->help);
|
|
Packit |
577717 |
rc = EINVAL;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rc = cmd->fn(num_tokens, tokens);
|
|
Packit |
577717 |
if (rc) {
|
|
Packit |
577717 |
LOG_ERROR("command '%s' (line %d) returned an error: "
|
|
Packit |
577717 |
"%d.", tokens[0], i+1, rc);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
free_lines(lines);
|
|
Packit |
577717 |
return rc;
|
|
Packit |
577717 |
}
|