Re: [PATCH 7/7] kernel-shark-qt: Add a plugin for sched events.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, 29 Aug 2018 19:42:24 +0300
"Yordan Karadzhov (VMware)" <y.karadz@xxxxxxxxx> wrote:

> --- /dev/null
> +++ b/kernel-shark-qt/src/plugins/SchedEvents.cpp
> @@ -0,0 +1,258 @@
> +// SPDX-License-Identifier: LGPL-2.1
> +
> +/*
> + * Copyright (C) 2018 VMware Inc, Yordan Karadzhov <y.karadz@xxxxxxxxx>
> + */
> +
> +/**
> + *  @file    SchedEvents.cpp
> + *  @brief   Plugin for Sched events.
> + */
> +
> +// KernelShark
> +#include "libkshark.h"
> +#include "plugins/sched_events.h"
> +#include "KsPlotTools.hpp"
> +#include "KsPlugins.hpp"
> +
> +extern struct plugin_sched_context *plugin_sched_context_handler;
> +
> +static int plugin_get_wakeup_pid_lazy(struct kshark_context *kshark_ctx,
> +				      const struct kshark_entry *e)
> +{
> +	struct plugin_sched_context *plugin_ctx;
> +	struct tep_record *record;
> +	unsigned long long val;
> +
> +	plugin_ctx = plugin_sched_context_handler;
> +	record = kshark_read_at(kshark_ctx, e->offset);
> +
> +	tep_read_number_field(plugin_ctx->sched_wakeup_pid_field,
> +				 record->data, &val);
> +	free(record);
> +
> +	return val;
> +}
> +
> +static void openBox(KsPlot::Rectangle **rec,
> +		    const KsPlot::Bin &bin,
> +		    int height)
> +{
> +	if (!*rec)
> +		*rec = new KsPlot::Rectangle;

BTW, for exception handling here (if new fails), should there be some
sort of comments somewhere, about what types of exceptions should be
caught. This is more a general question and not specific to this code
here.

With C, one needs to always check for the return. Now, with C++ we can
do try { } catch, but then we need to be able to clean everything up.

I honestly think that try/catch can end up being sloppier than the C
way, but C is a bit more work to get right.

> +
> +	(**rec).setFill(false);
> +	(**rec).setPoint(0, bin._base.x() - 1,
> +			    bin._base.y() - height);
> +
> +	(**rec).setPoint(1, bin._base.x() - 1,
> +			    bin._base.y() - 1);
> +}
> +
> +static void closeBox(KsPlot::Rectangle **rec,
> +		     const KsPlot::Bin &bin,
> +		     int height,
> +		     KsPlot::PlotObjList *list)
> +{
> +	if (*rec == nullptr)
> +		return;
> +
> +	if (bin._base.x() - (**rec).getPoint(0)->x < 4) {

Let's keep from using hard coded numbers. Why 4? We should make this at
least a macro that can be changed. Or perhaps a variable in some class?


> +		/* This box is too small.
> +		 * Don't try to plot it. */
> +		delete *rec;
> +		*rec = nullptr;
> +		return;
> +	}
> +
> +	(**rec).setPoint(3, bin._base.x() - 1,
> +			    bin._base.y() - height);
> +
> +	(**rec).setPoint(2, bin._base.x() - 1,
> +			    bin._base.y() - 1);
> +
> +	list->push_front(*rec);
> +	*rec = nullptr;
> +}
> +
> +static void schedWakeupPluginDraw(struct plugin_sched_context *plugin_ctx,
> +				  kshark_trace_histo *histo,
> +				  KsPlot::Graph *graph,
> +				  int pid,
> +				  KsPlot::PlotObjList *shapes)
> +{
> +	const kshark_entry *entryF, *entryB;
> +	KsPlot::Rectangle *rec = nullptr;
> +	kshark_context *kshark_ctx(NULL);
> +	kshark_entry_collection *col;
> +	kshark_instance(&kshark_ctx);
> +	int wakeup_pid;
> +
> +	col = kshark_find_data_collection(kshark_ctx->collections,
> +					  kshark_match_pid, pid);

If col is NULL, should we just return right away? Or is col OK to be
NULL?

> +
> +	size_t nBins = graph->size();
> +	for (size_t bin = 0; bin < nBins; ++bin) {
> +		if (ksmodel_bin_count(histo, bin) > 500)

Why 500?

> +			continue;
> +
> +		/*
> +		 * Starting from the first element in this bin, go forward
> +		 * in time until you find a trace entry that satisfies the
> +		 * condition defined by plugin_check_pid.
> +		 */
> +		entryF = ksmodel_get_entry_front(histo, bin, false,
> +						 kshark_match_pid, pid,
> +						 col, nullptr);
> +
> +		/*
> +		 * Starting from the last element in this bin, go backward
> +		 * in time until you find a trace entry that satisfies the
> +		 * condition defined by plugin_wakeup_check_pid.
> +		 */
> +		entryB = ksmodel_get_entry_back(histo, bin, false,
> +						plugin_wakeup_match_pid, pid,
> +						col, nullptr);
> +
> +		if (entryB &&
> +		    plugin_ctx->sched_wakeup_event &&
> +		    entryB->event_id == plugin_ctx->sched_wakeup_event->id) {
> +			wakeup_pid =
> +				plugin_get_wakeup_pid_lazy(kshark_ctx, entryB);
> +			if (wakeup_pid == pid) {
> +				/*
> +				 * entryB is a sched_wakeup_event. Open a
> +				 * green box here.
> +				 */
> +				openBox(&rec, graph->getBin(bin),
> +					      graph->getHeight() * .3);
> +
> +				 /* Green */
> +				rec->_color = KsPlot::Color(0, 255, 0);
> +			}
> +		}
> +
> +		if (entryF &&
> +		    plugin_ctx->sched_switch_event &&
> +		    entryF->event_id == plugin_ctx->sched_switch_event->id &&
> +		    entryF->pid == pid) {
> +			/*
> +			 * entryF is sched_switch_event. Close the box and add
> +			 * it to the list of shapes to be ploted.
> +			 */
> +			closeBox(&rec, graph->getBin(bin),
> +				       graph->getHeight() * .3,
> +				       shapes);
> +		}
> +	}
> +
> +	if (rec)
> +		delete rec;
> +
> +	return;
> +}
> +
> +static void schedSwitchPluginDraw(struct plugin_sched_context *plugin_ctx,
> +				  kshark_trace_histo *histo,
> +				  KsPlot::Graph *graph,
> +				  int pid,
> +				  KsPlot::PlotObjList *shapes)

This is quite similar to the function above, is there a way to combine
the two? This is C++ ;-)

-- Steve

> +{
> +	const kshark_entry *entryF, *entryB;
> +	KsPlot::Rectangle *rec = nullptr;
> +	kshark_context *kshark_ctx(NULL);
> +	kshark_entry_collection *col;
> +
> +	kshark_instance(&kshark_ctx);
> +	col = kshark_find_data_collection(kshark_ctx->collections,
> +					  kshark_match_pid, pid);
> +
> +	size_t nBins = graph->size();
> +	for (size_t bin = 0; bin < nBins; ++bin) {
> +		
> +		if (ksmodel_bin_count(histo, bin) > 500)
> +			continue;
> +
> +		/*
> +		 * Starting from the first element in this bin, go forward
> +		 * in time until you find a trace entry that satisfies the
> +		 * condition defined by kshark_match_pid.
> +		 */
> +		entryF = ksmodel_get_entry_front(histo, bin, false,
> +						 kshark_match_pid, pid,
> +						 col, nullptr);
> +
> +		/*
> +		 * Starting from the last element in this bin, go backward
> +		 * in time until you find a trace entry that satisfies the
> +		 * condition defined by plugin_switch_check_pid.
> +		 */
> +		entryB = ksmodel_get_entry_back(histo, bin, false,
> +						plugin_switch_match_pid, pid,
> +						col, nullptr);
> +
> +		if (entryB && entryB->pid != pid &&
> +		    entryB->event_id == plugin_ctx->sched_switch_event->id) {
> +			/*
> +			 * entryB is a sched_switch_event.
> +			 * Open a red box here.
> +			 */
> +			openBox(&rec, graph->getBin(bin),
> +				      graph->getHeight() * .3);
> +
> +			/* Red */
> +			rec->_color = KsPlot::Color(255, 0, 0);
> +		}
> +
> +		if (entryF && entryF->pid == pid &&
> +		    entryF->event_id == plugin_ctx->sched_switch_event->id) {
> +			/*
> +			 * entryF is sched_switch_event.
> +			 * Close the box and add it to the list
> +			 * of shapes to be ploted.
> +			 */
> +			closeBox(&rec, graph->getBin(bin),
> +				       graph->getHeight() * .3,
> +				       shapes);
> +		}
> +	}
> +
> +	if (rec)
> +		delete rec;
> +
> +	return;
> +}
> +
> +/**
> + * @brief Plugin's draw function.
> + *
> + * @param argv_c: A C pointer to be converted to KsCppArgV (C++ struct).
> + * @param pid: Process Id.
> + * @param draw_action: Draw action identifier.
> + *
> + * @returns True if the Pid of the entry matches the value of "pid".
> + *	    Otherwise false.
> + */
> +void plugin_draw(struct kshark_cpp_argv *argv_c, int pid, int draw_action)
> +{
> +	if (draw_action != KSHARK_PLUGIN_TASK_DRAW || pid == 0)
> +		return;
> +
> +	struct plugin_sched_context *plugin_ctx =
> +		plugin_sched_context_handler;
> +
> +	if (!plugin_ctx)
> +		return;
> +
> +	KsCppArgV *argvCpp = KS_ARGV_TO_CPP(argv_c);
> +
> +	schedWakeupPluginDraw(plugin_ctx, argvCpp->_histo,
> +					  argvCpp->_graph,
> +					  pid,
> +					  argvCpp->_shapes);
> +
> +	schedSwitchPluginDraw(plugin_ctx, argvCpp->_histo,
> +					  argvCpp->_graph,
> +					  pid,
> +					  argvCpp->_shapes);
> +}
> diff --git a/kernel-shark-qt/src/plugins/sched_events.c b/kernel-shark-qt/src/plugins/sched_events.c
> new file mode 100644
> index 0000000..2885a22
> --- /dev/null
> +++ b/kernel-shark-qt/src/plugins/sched_events.c
> @@ -0,0 +1,302 @@
> +// SPDX-License-Identifier: LGPL-2.1
> +
> +/*
> + * Copyright (C) 2018 VMware Inc, Yordan Karadzhov <y.karadz@xxxxxxxxx>
> + */
> +
> +/**
> + *  @file    sched_events.c
> + *  @brief   Plugin for Sched events.
> + */
> +
> +// C
> +#include <stdlib.h>
> +#include <stdio.h>
> +
> +// KernelShark
> +#include "plugins/sched_events.h"
> +
> +/** Plugin context instance. */
> +struct plugin_sched_context *plugin_sched_context_handler = NULL;
> +
> +static bool plugin_sched_update_context(struct kshark_context *kshark_ctx)
> +{
> +	struct plugin_sched_context *plugin_ctx;
> +	struct event_format *event;
> +
> +	if (!plugin_sched_context_handler) {
> +		plugin_sched_context_handler =
> +		 malloc(sizeof(*plugin_sched_context_handler));
> +	}
> +
> +	plugin_ctx = plugin_sched_context_handler;
> +	plugin_ctx->handle = kshark_ctx->handle;
> +	plugin_ctx->pevent = kshark_ctx->pevent;
> +
> +	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");
> +
> +	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");
> +
> +	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;
> +
> +	tep_read_number_field(plugin_ctx->sched_switch_next_field,
> +				 record->data, &val);
> +	return val;
> +}
> +
> +/**
> + * @brief Get the Process Id of the task being woke up.
> + *
> + * @param record: Input location for a sched_wakeup record.
> + */
> +int plugin_get_wakeup_pid(struct tep_record *record)
> +{
> +	struct plugin_sched_context *plugin_ctx =
> +		plugin_sched_context_handler;
> +	unsigned long long val;
> +
> +	tep_read_number_field(plugin_ctx->sched_wakeup_pid_field,
> +				 record->data, &val);
> +	return 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) {
> +			comm = record->data +
> +			       plugin_ctx->sched_switch_comm_field->offset;
> +	}
> +
> +	if (!tep_pid_is_registered(kshark_ctx->pevent, pid))
> +			tep_register_comm(kshark_ctx->pevent,
> +					     comm, pid);
> +}
> +
> +static int plugin_get_wakeup_new_pid(struct tep_record *record)
> +{
> +	struct plugin_sched_context *plugin_ctx =
> +		plugin_sched_context_handler;
> +	unsigned long long val;
> +
> +	tep_read_number_field(plugin_ctx->sched_wakeup_new_pid_field,
> +				 record->data, &val);
> +
> +	return 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 entry matches the value of "pid".
> + *	    Otherwise false.
> + */
> +bool plugin_wakeup_match_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 wakeup_pid = -1;
> +
> +	if (e->pid == pid)
> +		return true;
> +
> +	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 = kshark_read_at(kshark_ctx, e->offset);
> +
> +		/* We only want those that actually woke up the task. */
> +		tep_read_number_field(plugin_ctx->sched_wakeup_success_field,
> +					 record->data, &val);
> +
> +		if (val)
> +			wakeup_pid = plugin_get_wakeup_pid(record);
> +	}
> +
> +	if (plugin_ctx->sched_wakeup_new_event &&
> +	    e->event_id == plugin_ctx->sched_wakeup_new_event->id) {
> +		record = kshark_read_at(kshark_ctx, e->offset);
> +
> +		/* We only want those that actually woke up the task. */
> +		tep_read_number_field(plugin_ctx->sched_wakeup_new_success_field,
> +					 record->data, &val);
> +
> +		if (val)
> +			wakeup_pid = plugin_get_wakeup_new_pid(record);
> +	}
> +
> +	free(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 entry matches the value of "pid".
> + *	    Otherwise false.
> + */
> +bool plugin_switch_match_pid(struct kshark_context *kshark_ctx,
> +			     struct kshark_entry *e,
> +			     int pid)
> +{
> +	struct plugin_sched_context *plugin_ctx;
> +	struct tep_record *record = NULL;
> +	int switch_pid = -1;
> +
> +	if (e->pid == pid)
> +		return true;
> +
> +	plugin_ctx = plugin_sched_context_handler;
> +
> +	if (plugin_ctx->sched_switch_event &&
> +	    e->event_id == plugin_ctx->sched_switch_event->id) {
> +		record = kshark_read_at(kshark_ctx, e->offset);
> +
> +		switch_pid = tep_data_pid(plugin_ctx->pevent, record);
> +	}
> +
> +	free(record);
> +
> +	if (switch_pid >= 0 && switch_pid == pid)
> +		return true;
> +
> +	return false;
> +}
> +
> +static void plugin_sched_action(struct kshark_context *kshark_ctx,
> +				struct tep_record *rec,
> +				struct kshark_entry *entry)
> +{
> +	entry->pid = plugin_get_next_pid(rec);
> +	plugin_register_command(kshark_ctx, rec, entry->pid);
> +}
> +
> +static void plugin_sched_load()
> +{
> +	struct kshark_context *kshark_ctx = NULL;
> +	struct plugin_sched_context *plugin_ctx;
> +
> +	kshark_instance(&kshark_ctx);
> +
> +	if (!plugin_sched_update_context(kshark_ctx)) {
> +		free(plugin_sched_context_handler);
> +		plugin_sched_context_handler = NULL;
> +		return;
> +	}
> +
> +	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);
> +}
> +
> +static void plugin_sched_unload()
> +{
> +	struct kshark_context *kshark_ctx = NULL;
> +	struct plugin_sched_context *plugin_ctx;
> +
> +	if (!plugin_sched_context_handler)
> +		return;
> +
> +	plugin_ctx = plugin_sched_context_handler;
> +	kshark_instance(&kshark_ctx);
> +
> +	if (kshark_ctx) {
> +		kshark_unregister_event_handler(&kshark_ctx->event_handlers,
> +						plugin_ctx->sched_switch_event->id,
> +						plugin_sched_action,
> +						plugin_draw);
> +	}
> +
> +	free(plugin_ctx);
> +	plugin_sched_context_handler = NULL;
> +}
> +
> +/** Load this plugin. */
> +void KSHARK_PLUGIN_LOADER()
> +{
> +	plugin_sched_load();
> +}
> +
> +/** Reload this plugin. */
> +void KSHARK_PLUGIN_RELOADER()
> +{
> +	plugin_sched_unload();
> +	plugin_sched_load();
> +}
> +
> +/** Unload this plugin. */
> +void KSHARK_PLUGIN_UNLOADER()
> +{
> +	plugin_sched_unload();
> +}
> diff --git a/kernel-shark-qt/src/plugins/sched_events.h b/kernel-shark-qt/src/plugins/sched_events.h
> new file mode 100644
> index 0000000..d0c125a
> --- /dev/null
> +++ b/kernel-shark-qt/src/plugins/sched_events.h
> @@ -0,0 +1,79 @@
> +/* SPDX-License-Identifier: LGPL-2.1 */
> +
> +/*
> + * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@xxxxxxxxx>
> + */
> +
> +/**
> + *  @file    sched_events.h
> + *  @brief   Plugin for Sched events.
> + */
> +
> +#ifndef _KS_PLUGIN_SHED_H
> +#define _KS_PLUGIN_SHED_H
> +
> +// trace-cmd
> +#include "event-parse.h"
> +
> +// KernelShark
> +#include "libkshark.h"
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/** Structure representing a plugin-specific context. */
> +struct plugin_sched_context {
> +	/** Input handle for the trace data file. */
> +	struct tracecmd_input	*handle;
> +
> +	/** Page event used to parse the page. */
> +	struct tep_handle	*pevent;
> +
> +	/** Pointer to the sched_switch_event object. */
> +	struct event_format	*sched_switch_event;
> +
> +	/** Pointer to the sched_switch_next_field format descriptor. */
> +	struct format_field	*sched_switch_next_field;
> +
> +	/** Pointer to the sched_switch_comm_field format descriptor. */
> +	struct format_field	*sched_switch_comm_field;
> +
> +	/** Pointer to the sched_wakeup_event object. */
> +	struct event_format	*sched_wakeup_event;
> +
> +	/** Pointer to the sched_wakeup_pid_field format descriptor. */
> +	struct format_field	*sched_wakeup_pid_field;
> +
> +	/** Pointer to the sched_wakeup_success_field format descriptor. */
> +	struct format_field	*sched_wakeup_success_field;
> +
> +	/** Pointer to the sched_wakeup_new_event object. */
> +	struct event_format	*sched_wakeup_new_event;
> +
> +	/** Pointer to the sched_wakeup_new_pid_field format descriptor. */
> +	struct format_field	*sched_wakeup_new_pid_field;
> +
> +	/**
> +	 * Pointer to the sched_wakeup_new_success_field format descriptor.
> +	 */
> +	struct format_field	*sched_wakeup_new_success_field;
> +};
> +
> +int plugin_get_next_pid(struct tep_record *record);
> +
> +int plugin_get_wakeup_pid(struct tep_record *record);
> +
> +bool plugin_wakeup_match_pid(struct kshark_context *kshark_ctx,
> +			     struct kshark_entry *e, int pid);
> +
> +bool plugin_switch_match_pid(struct kshark_context *kshark_ctx,
> +			     struct kshark_entry *e, int pid);
> +
> +void plugin_draw(struct kshark_cpp_argv *argv, int pid, int draw_action);
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif




[Index of Archives]     [Linux USB Development]     [Linux USB Development]     [Linux Audio Users]     [Yosemite Hiking]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux