blob: c0328e804911350d5993cd7b74c77df562650ecf [file] [log] [blame]
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2018 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
*/
/**
* @file sched_events.c
* @brief Defines a callback function for Sched events used to registers the
* "next" task (if not registered already) and to changes the value
* of the "pid" field of the "sched_switch" entries such that, it
* will be ploted as part of the "next" task.
*/
// C
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
// KernelShark
#include "plugins/sched_events.h"
/** Plugin context instance. */
struct plugin_sched_context *plugin_sched_context_handler = NULL;
static bool plugin_sched_init_context(struct kshark_context *kshark_ctx)
{
struct plugin_sched_context *plugin_ctx;
struct tep_event *event;
/* No context should exist when we initialize the plugin. */
assert(plugin_sched_context_handler == NULL);
plugin_sched_context_handler =
malloc(sizeof(*plugin_sched_context_handler));
if (!plugin_sched_context_handler) {
fprintf(stderr,
"Failed to allocate memory for plugin_sched_context.\n");
return false;
}
plugin_ctx = plugin_sched_context_handler;
plugin_ctx->handle = kshark_ctx->handle;
plugin_ctx->pevent = kshark_ctx->pevent;
plugin_ctx->collections = NULL;
event = tep_find_event_by_name(plugin_ctx->pevent,
"sched", "sched_switch");
if (!event)
return false;
plugin_ctx->sched_switch_event = event;
plugin_ctx->sched_switch_next_field =
tep_find_any_field(event, "next_pid");
plugin_ctx->sched_switch_comm_field =
tep_find_field(event, "next_comm");
plugin_ctx->sched_switch_prev_state_field =
tep_find_field(event, "prev_state");
event = tep_find_event_by_name(plugin_ctx->pevent,
"sched", "sched_wakeup");
if (!event)
return false;
plugin_ctx->sched_wakeup_event = event;
plugin_ctx->sched_wakeup_pid_field =
tep_find_any_field(event, "pid");
plugin_ctx->sched_wakeup_success_field =
tep_find_field(event, "success");
event = tep_find_event_by_name(plugin_ctx->pevent,
"sched", "sched_wakeup_new");
if (!event)
return false;
plugin_ctx->sched_wakeup_new_event = event;
plugin_ctx->sched_wakeup_new_pid_field =
tep_find_any_field(event, "pid");
plugin_ctx->sched_wakeup_new_success_field =
tep_find_field(event, "success");
plugin_ctx->second_pass_hash = tracecmd_filter_id_hash_alloc();
return true;
}
/**
* @brief Get the Process Id of the next scheduled task.
*
* @param record: Input location for a sched_switch record.
*/
int plugin_get_next_pid(struct tep_record *record)
{
struct plugin_sched_context *plugin_ctx =
plugin_sched_context_handler;
unsigned long long val;
int ret;
ret = tep_read_number_field(plugin_ctx->sched_switch_next_field,
record->data, &val);
return ret ? : val;
}
/**
* @brief Get the Process Id of the task being woke up.
*
* @param record: Input location for a sched_wakeup record.
*/
int plugin_get_rec_wakeup_pid(struct tep_record *record)
{
struct plugin_sched_context *plugin_ctx =
plugin_sched_context_handler;
unsigned long long val;
int ret;
ret = tep_read_number_field(plugin_ctx->sched_wakeup_pid_field,
record->data, &val);
return ret ? : val;
}
static void plugin_register_command(struct kshark_context *kshark_ctx,
struct tep_record *record,
int pid)
{
struct plugin_sched_context *plugin_ctx =
plugin_sched_context_handler;
const char *comm;
if (!plugin_ctx->sched_switch_comm_field)
return;
comm = record->data + plugin_ctx->sched_switch_comm_field->offset;
/*
* TODO: The retrieve of the name of the command above needs to be
* implemented as a wrapper function in libtracevent.
*/
if (!tep_pid_is_registered(kshark_ctx->pevent, pid))
tep_register_comm(kshark_ctx->pevent, comm, pid);
}
static int plugin_get_rec_wakeup_new_pid(struct tep_record *record)
{
struct plugin_sched_context *plugin_ctx =
plugin_sched_context_handler;
unsigned long long val;
int ret;
ret = tep_read_number_field(plugin_ctx->sched_wakeup_new_pid_field,
record->data, &val);
return ret ? : val;
}
/**
* @brief Process Id matching function adapted for sched_wakeup and
* sched_wakeup_new events.
*
* @param kshark_ctx: Input location for the session context pointer.
* @param e: kshark_entry to be checked.
* @param pid: Matching condition value.
*
* @returns True if the Pid of the record matches the value of "pid".
* Otherwise false.
*/
bool plugin_wakeup_match_rec_pid(struct kshark_context *kshark_ctx,
struct kshark_entry *e,
int pid)
{
struct plugin_sched_context *plugin_ctx;
struct tep_record *record = NULL;
unsigned long long val;
int ret, wakeup_pid = -1;
plugin_ctx = plugin_sched_context_handler;
if (!plugin_ctx)
return false;
if (plugin_ctx->sched_wakeup_event &&
e->event_id == plugin_ctx->sched_wakeup_event->id) {
record = tracecmd_read_at(kshark_ctx->handle, e->offset, NULL);
/* We only want those that actually woke up the task. */
ret = tep_read_number_field(plugin_ctx->sched_wakeup_success_field,
record->data, &val);
if (ret == 0 && val)
wakeup_pid = plugin_get_rec_wakeup_pid(record);
}
if (plugin_ctx->sched_wakeup_new_event &&
e->event_id == plugin_ctx->sched_wakeup_new_event->id) {
record = tracecmd_read_at(kshark_ctx->handle, e->offset, NULL);
/* We only want those that actually woke up the task. */
ret = tep_read_number_field(plugin_ctx->sched_wakeup_new_success_field,
record->data, &val);
if (ret == 0 && val)
wakeup_pid = plugin_get_rec_wakeup_new_pid(record);
}
free_record(record);
if (wakeup_pid >= 0 && wakeup_pid == pid)
return true;
return false;
}
/**
* @brief Process Id matching function adapted for sched_switch events.
*
* @param kshark_ctx: Input location for the session context pointer.
* @param e: kshark_entry to be checked.
* @param pid: Matching condition value.
*
* @returns True if the Pid of the record matches the value of "pid".
* Otherwise false.
*/
bool plugin_switch_match_rec_pid(struct kshark_context *kshark_ctx,
struct kshark_entry *e,
int pid)
{
struct plugin_sched_context *plugin_ctx;
unsigned long long val;
int ret, switch_pid = -1;
plugin_ctx = plugin_sched_context_handler;
if (plugin_ctx->sched_switch_event &&
e->event_id == plugin_ctx->sched_switch_event->id) {
struct tep_record *record;
record = tracecmd_read_at(kshark_ctx->handle, e->offset, NULL);
ret = tep_read_number_field(plugin_ctx->sched_switch_prev_state_field,
record->data, &val);
if (ret == 0 && !(val & 0x7f))
switch_pid = tep_data_pid(plugin_ctx->pevent, record);
free_record(record);
}
if (switch_pid >= 0 && switch_pid == pid)
return true;
return false;
}
/**
* @brief Process Id matching function adapted for sched_switch events.
*
* @param kshark_ctx: Input location for the session context pointer.
* @param e: kshark_entry to be checked.
* @param pid: Matching condition value.
*
* @returns True if the Pid of the entry matches the value of "pid".
* Otherwise false.
*/
bool plugin_switch_match_entry_pid(struct kshark_context *kshark_ctx,
struct kshark_entry *e,
int pid)
{
struct plugin_sched_context *plugin_ctx;
plugin_ctx = plugin_sched_context_handler;
if (plugin_ctx->sched_switch_event &&
e->event_id == plugin_ctx->sched_switch_event->id &&
e->pid == pid)
return true;
return false;
}
/**
* @brief A match function to be used to process a data collections for
* the Sched events plugin.
*
* @param kshark_ctx: Input location for the session context pointer.
* @param e: kshark_entry to be checked.
* @param pid: Matching condition value.
*
* @returns True if the entry is relevant for the Sched events plugin.
* Otherwise false.
*/
bool plugin_match_pid(struct kshark_context *kshark_ctx,
struct kshark_entry *e, int pid)
{
return plugin_switch_match_entry_pid(kshark_ctx, e, pid) ||
plugin_switch_match_rec_pid(kshark_ctx, e, pid) ||
plugin_wakeup_match_rec_pid(kshark_ctx, e, pid);
}
static void plugin_sched_action(struct kshark_context *kshark_ctx,
struct tep_record *rec,
struct kshark_entry *entry)
{
int pid = plugin_get_next_pid(rec);
if (pid >= 0) {
entry->pid = pid;
plugin_register_command(kshark_ctx, rec, entry->pid);
}
}
static int plugin_sched_init(struct kshark_context *kshark_ctx)
{
struct plugin_sched_context *plugin_ctx;
if (!plugin_sched_init_context(kshark_ctx)) {
free(plugin_sched_context_handler);
plugin_sched_context_handler = NULL;
return 0;
}
plugin_ctx = plugin_sched_context_handler;
kshark_register_event_handler(&kshark_ctx->event_handlers,
plugin_ctx->sched_switch_event->id,
plugin_sched_action,
plugin_draw);
return 1;
}
static int plugin_sched_close(struct kshark_context *kshark_ctx)
{
struct plugin_sched_context *plugin_ctx;
if (!plugin_sched_context_handler)
return 0;
plugin_ctx = plugin_sched_context_handler;
kshark_unregister_event_handler(&kshark_ctx->event_handlers,
plugin_ctx->sched_switch_event->id,
plugin_sched_action,
plugin_draw);
tracecmd_filter_id_hash_free(plugin_ctx->second_pass_hash);
kshark_free_collection_list(plugin_ctx->collections);
free(plugin_ctx);
plugin_sched_context_handler = NULL;
return 1;
}
/** Load this plugin. */
int KSHARK_PLUGIN_INITIALIZER(struct kshark_context *kshark_ctx)
{
return plugin_sched_init(kshark_ctx);
}
/** Unload this plugin. */
int KSHARK_PLUGIN_DEINITIALIZER(struct kshark_context *kshark_ctx)
{
return plugin_sched_close(kshark_ctx);
}