* Re: [Powertop] [FYI][RFC][PATCH 2/2] MALI GPU profiling support
@ 2012-12-25 20:58 Sergey Senozhatsky
0 siblings, 0 replies; 2+ messages in thread
From: Sergey Senozhatsky @ 2012-12-25 20:58 UTC (permalink / raw)
To: powertop
[-- Attachment #1: Type: text/plain, Size: 48245 bytes --]
Hello Igor,
On (11/01/12 16:00), Igor Zhbanov wrote:
> This patch adds MALI GPU profiling support.
> The information is collected from internal tracing mechanism
> (/sys/kernel/debug/mali/profiling/).
>
> Currently the collected data is written in reports only and not
> on screen in interactive mode.
>
> Reviewed-by: Kyungmin Park <kyungmin.park(a)samsung.com>
> ---
> src/Makefile.am | 1 +
> src/main.cpp | 8 +
> src/mali-internal-events/mali-events.h | 143 +++
> src/mali-internal-events/mali-internal-events.cpp | 1279 +++++++++++++++++++++
> 4 files changed, 1431 insertions(+), 0 deletions(-)
> create mode 100644 src/mali-internal-events/mali-events.h
> create mode 100644 src/mali-internal-events/mali-internal-events.cpp
>
> diff --git a/src/Makefile.am b/src/Makefile.am
> index e3eb6c2..af98867 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -28,6 +28,7 @@ powertop_SOURCES = parameters/persistent.cpp parameters/learn.cpp parameters/par
> tuning/tuningsysfs.cpp tuning/wifi.h tuning/runtime.cpp tuning/tunable.h \
> tuning/runtime.h tuning/tuningusb.h tuning/iw.h calibrate/calibrate.cpp \
> calibrate/calibrate.h measurement/measurement.cpp measurement/power_supply.cpp \
> + mali-internal-events/mali-events.h mali-internal-events/mali-internal-events.cpp \
> mali-internal-events/proc-scan.cpp mali-internal-events/proc-scan.h \
> measurement/measurement.h measurement/acpi.cpp measurement/sysfs.h measurement/sysfs.cpp \
> measurement/acpi.h measurement/extech.cpp measurement/power_supply.h measurement/extech.h \
> diff --git a/src/main.cpp b/src/main.cpp
> index 83adfad..c901ce7 100644
> --- a/src/main.cpp
> +++ b/src/main.cpp
> @@ -61,6 +61,7 @@
> #define DEBUGFS_MAGIC 0x64626720
>
> #include "mali-internal-events/proc-scan.h"
> +#include "mali-internal-events/mali-events.h"
>
> int debug_learning = 0;
> unsigned time_out = 20;
> @@ -183,6 +184,7 @@ static void do_sleep(int seconds)
> } while (1);
> }
>
> +mali_ctx *ctx = NULL;
>
do we really need mali_ctx in main all the time (even for !mali builds)?
can we hide mali context as a static within mali internals?
side note: '#define __arm__' implies '#define mali'.
> void one_measurement(int seconds, char *workload)
> {
> @@ -191,6 +193,7 @@ void one_measurement(int seconds, char *workload)
> devices_start_measurement();
> start_process_measurement();
> start_cpu_measurement();
> + start_mali_capture(ctx);
>
> if (workload && workload[0]) {
> scanproc(0, 0);
> @@ -206,9 +209,11 @@ void one_measurement(int seconds, char *workload)
> collect_open_devices();
> devices_end_measurement();
> end_power_measurement();
> + stop_mali_capture(ctx);
>
> process_cpu_data();
> process_process_data();
> + process_mali_events(ctx);
>
> /* output stats */
> process_update_display();
> @@ -257,11 +262,14 @@ void make_report(int time, char *workload, int iterations, char *file)
> for (int i=0; i != iterations; i++){
> init_report_output(file, iterations);
> initialize_tuning();
> + ctx = init_mali_capture();
> /* and then the real measurement */
> one_measurement(time, workload);
> report_show_tunables();
> + print_mali_stat(ctx);
> finish_report_output();
> clear_tuning();
> + cleanup_mali_capture(&ctx);
> }
> /* and wrap up */
> learn_parameters(50, 0);
> diff --git a/src/mali-internal-events/mali-events.h b/src/mali-internal-events/mali-events.h
> new file mode 100644
> index 0000000..825f926
> --- /dev/null
> +++ b/src/mali-internal-events/mali-events.h
> @@ -0,0 +1,143 @@
> +/* Copyright (c) 2012 Samsung Electronics Co., Ltd.
> + * http://www.samsung.com/
> + *
> + * This file is part of PowerTOP
> + *
> + * This program file is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> + * for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program in a file named COPYING; if not, write to the
> + * Free Software Foundation, Inc,
> + * 51 Franklin Street, Fifth Floor,
> + * Boston, MA 02110-1301 USA
> + * or just google for it.
> + *
> + * MALI GPU internal events tracer for r3p0 driver version.
> + * Written by Igor Zhbanov <i.zhbanov(a)samsung.com>,
> + * Alina Litvinova <alina.litvinova(a)gmail.com>
> + * 2012.06 */
> +
> +#ifndef _MALI_EVENTS_H_
> +#define _MALI_EVENTS_H_
> +
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <sys/types.h>
> +
> +struct mali_event {
> + unsigned long long timestamp;
> + unsigned int type, channel, data, d0, d1, d2, d3, d4;
> +};
> +
> +/*
> +Event ID bit format:
> + 3 2 1
> +10987654321098765432109876543210
> +RRRRTTTTCCCCCCCCDDDDDDDDDDDDDDDD
> +
> +RRRR -- reserved for future use.
> +
> +TTTT -- type of the event: */
> +#define MALI_PROFILING_EVENT_TYPE_SINGLE 0
> +#define MALI_PROFILING_EVENT_TYPE_START 1
> +#define MALI_PROFILING_EVENT_TYPE_STOP 2
> +#define MALI_PROFILING_EVENT_TYPE_SUSPEND 3
> +#define MALI_PROFILING_EVENT_TYPE_RESUME 4
> +
> +/*
> +CCCCCCCC -- channel/source of the event: */
> +#define MALI_PROFILING_EVENT_CHANNEL_SOFTWARE 0
> +#define MALI_PROFILING_EVENT_CHANNEL_GP0 1
> +#define MALI_PROFILING_EVENT_CHANNEL_PP0 5
> +#define MALI_PROFILING_EVENT_CHANNEL_PP1 6
> +#define MALI_PROFILING_EVENT_CHANNEL_PP2 7
> +#define MALI_PROFILING_EVENT_CHANNEL_PP3 8
> +#define MALI_PROFILING_EVENT_CHANNEL_PP4 9
> +#define MALI_PROFILING_EVENT_CHANNEL_PP5 10
> +#define MALI_PROFILING_EVENT_CHANNEL_PP6 11
> +#define MALI_PROFILING_EVENT_CHANNEL_PP7 12
> +#define MALI_PROFILING_EVENT_CHANNEL_GPU 21
> +
> +#define GP_NUM 1 /* Maximal number of GP units */
> +#define PP_NUM 8 /* Maximal number of PP units */
> +
> +/*
> +DDDDDDDDDDDDDDDD -- event data.
> +
> +Events data for MALI_PROFILING_EVENT_TYPE_SINGLE event type
> +from software channel: */
> +#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_NONE 0 /* Not used */,
> +#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_NEW_FRAME 1 /* Not used */,
> +#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_FLUSH 2 /* Not used */,
> +#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_SWAP_BUFFERS 3 /* Not used */,
> +/*#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_FB_EVENT 4 // NOT USED */
> +#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_GPU_FREQ 4
> +#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_GPU_VOLTS 5
> +#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_GLOBAL_TRY_LOCK 6
> +#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_GLOBAL_LOCK 7
> +#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_GLOBAL_UNLOCK 8
> +#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_VBLANK_VSYNC 9
> +
> +/*
> +Events data for MALI_PROFILING_EVENT_TYPE_SINGLE event type
> +from a HW channel (GPx + PPx + GPU): */
> +#define MALI_PROFILING_EVENT_REASON_SINGLE_HW_NONE 0 /* Not used */
> +#define MALI_PROFILING_EVENT_REASON_SINGLE_HW_INTERRUPT 1
> +#define MALI_PROFILING_EVENT_REASON_SINGLE_HW_FLUSH 2
> +#define MALI_PROFILING_EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE 10
> +
> +#define TIMESTAMP_FACTOR 1000000000ll
> +
> +#define MALI_PROFILING_PATH "/sys/kernel/debug/mali/profiling/"
> +#define MALI_PROFILING_EVENTS MALI_PROFILING_PATH "events"
> +#define MALI_PROFILING_RECORD MALI_PROFILING_PATH "record"
> +#define MALI_PROFILING_ENABLE MALI_PROFILING_PATH "proc/default/enable"
> +
> +enum unit_status {
> + UNIT_FREE = 0,
> + UNIT_BUSY
> +};
> +
> +struct unit_state {
> + long long job_start;
> + pid_t pid, tid;
> + unit_status status;
> +};
> +
> +struct mali_state {
> + unit_state gp[GP_NUM];
> + unit_state pp[PP_NUM];
> + int freq, volt; /* I hope GPU frequency will not be above 2 GHz ;-) */
> + int split_count;
> +};
> +
> +struct mali_ctx {
> + int old_enable, old_record;
> + /* For filtering events timestamp */
> + long long start_time, finish_time;
> + const char *fname;
> + mali_state gpu_state;
> + bool live, error_flag, started, processed;
> +};
> +
> +mali_ctx *init_mali_capture();
> +bool start_mali_capture(mali_ctx *ctx);
> +bool stop_mali_capture(mali_ctx *ctx);
> +bool process_mali_events(mali_ctx *ctx);
> +bool print_mali_stat(mali_ctx *ctx);
> +bool cleanup_mali_capture(mali_ctx **ctx);
> +
> +bool get_pt_proc_info(pid_t pid, const char **name, const char **cmdline);
> +
> +#ifndef UNUSED
> +#define UNUSED __attribute__((unused))
> +#endif /* UNUSED */
> +
so we know everything about mali even in !mali (!arm) builds?
> +#endif /* _MALI_EVENTS_H_ */
> diff --git a/src/mali-internal-events/mali-internal-events.cpp b/src/mali-internal-events/mali-internal-events.cpp
> new file mode 100644
> index 0000000..686d1c3
> --- /dev/null
> +++ b/src/mali-internal-events/mali-internal-events.cpp
> @@ -0,0 +1,1279 @@
> +/* Copyright (c) 2012 Samsung Electronics Co., Ltd.
> + * http://www.samsung.com/
> + *
> + * This file is part of PowerTOP
> + *
> + * This program file is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> + * for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program in a file named COPYING; if not, write to the
> + * Free Software Foundation, Inc,
> + * 51 Franklin Street, Fifth Floor,
> + * Boston, MA 02110-1301 USA
> + * or just google for it.
> + *
> + * MALI GPU internal events tracer for r3p0 driver version.
> + * Written by Igor Zhbanov <i.zhbanov(a)samsung.com>,
> + * Alina Litvinova <alina.litvinova(a)gmail.com>
> + * 2012.06 */
> +
> +#define _POSIX_SOURCE
> +
> +#include <time.h>
> +#include <stdio.h>
> +#include <locale.h>
> +#include <stdarg.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <sys/time.h>
> +#include <sys/types.h>
> +
> +#include "proc-scan.h"
> +#include "mali-events.h"
> +
> +#include "../report/report.h"
> +#include "../report/report-maker.h"
> +
> +#ifdef __arm__
> +
> +enum debug_level_t {
> + NO_DEBUG = 0,
> + CRIT,
> + TOTAL_REPORT,
> + WARN,
> + PER_UNIT_REPORT,
> + TASK_EVENTS,
> + MISC_EVENTS
> +};
> +
> +static debug_level_t debug_level = CRIT;
> +
> +/* ************************************************************************ */
> +
> +static int dprintf(debug_level_t level, const char *fmt, ...)
> + __attribute__ ((format (printf, 2, 3)));
> +
> +static int
> +dprintf(debug_level_t level, const char *fmt, ...) {
> + int ret;
> +
> + if (level > debug_level)
> + return 0;
> +
> + va_list ap;
> + va_start(ap, fmt);
> + ret = vprintf(fmt, ap);
> + va_end(ap);
> + return ret;
> +}
> +
> +/* ************************************************************************ */
> +
> +static int edprintf(debug_level_t level, const char *fmt, ...)
> + __attribute__ ((format (printf, 2, 3)));
> +
> +static int
> +edprintf(debug_level_t level, const char *fmt, ...) {
> + int ret;
> +
> + if (level > debug_level)
> + return 0;
> +
> + va_list ap;
> + va_start(ap, fmt);
> + ret = vfprintf(stderr, fmt, ap);
> + va_end(ap);
> + return ret;
> +}
> +
side note: to my mind, it's better to have some debugging support in lib, rather than
inside some subsystem.
off topic: I think it'll be nice to have some UI level user reporting, let's say,
first UI line (white text on red bg).
e.g.
if (toggle() != SUCCESS)
notify_user("Unable to toggle powersave on %s", _(dev_name));
for example, something like this (let's return to this later):
void notify_user(const char *frmt, ...)
{
va_list list;
start_color();
init_pair(1, COLOR_WHITE, COLOR_RED);
va_start(list, frmt);
attron(COLOR_PAIR(1));
mvprintw(1, 0, frmt, list);
attroff(COLOR_PAIR(1));
va_end(list);
}
> +/* ************************************************************************ */
> +
> +static int
> +get_int_value(const char *fname)
> +{
> + FILE *f;
> + int value;
> +
> + f = fopen(fname, "r");
> + if (!f) {
> + edprintf(CRIT, "Can't open \"%s\".\n", fname);
> + return -1;
> + }
> +
> + if (fscanf(f, "%d", &value) != 1) {
> + edprintf(CRIT, "Can't read \"%s\" or its value is in wrong "
> + "format.\n", fname);
> + fclose(f);
> + return -1;
> + }
> +
> + fclose(f);
> + return value;
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +set_int_value(const char *fname, int value)
> +{
> + FILE *f;
> +
> + f = fopen(fname, "w");
> + if (!f) {
> + edprintf(CRIT, "Can't open \"%s\".\n", fname);
> + return false;
> + }
> +
> + fprintf(f, "%d\n", value);
> + fclose(f);
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +static int
> +get_enable()
> +{
> + return get_int_value(MALI_PROFILING_ENABLE);
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +set_enable(int value)
> +{
> + return set_int_value(MALI_PROFILING_ENABLE, value);
> +}
> +
> +/* ************************************************************************ */
> +
> +static int
> +get_record()
> +{
> + return get_int_value(MALI_PROFILING_RECORD);
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +set_record(int value)
> +{
> + return set_int_value(MALI_PROFILING_RECORD, value);
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +save_mode(int *old_enable, int *old_record)
> +{
> + *old_enable = get_enable();
> + if (*old_enable == -1) {
> + edprintf(CRIT, "Can't get value of \"" MALI_PROFILING_ENABLE
> + "\".\n");
> + return false;
> + }
> +
> + *old_record = get_record();
> + if (*old_record == -1) {
> + edprintf(CRIT, "Can't get value of \"" MALI_PROFILING_RECORD
> + "\".\n");
> + return false;
> + }
> +
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +set_mode()
> +{
> + if (!set_enable(1))
> + return false;
> +
> + /* Enable recording. Don't clean previous records to collect
> + * voltage and frequency information */
> + if (!set_record(1))
> + return false;
> +
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +restore_mode(int old_enable, int old_record)
> +{
> + /* Clear previous events and disable recording */
> + if (!set_record(old_record))
> + return false;
> +
> + if (!set_enable(old_enable))
> + return false;
> +
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +static int
> +gp_number(int channel)
> +{
> + return channel - MALI_PROFILING_EVENT_CHANNEL_GP0;
> +}
> +
> +/* ************************************************************************ */
> +
> +static int
> +pp_number(int channel)
> +{
> + return channel - MALI_PROFILING_EVENT_CHANNEL_PP0;
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +is_gp_channel(int channel)
> +{
> + return (channel == MALI_PROFILING_EVENT_CHANNEL_GP0);
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +is_pp_channel(int channel)
> +{
> + return (channel == MALI_PROFILING_EVENT_CHANNEL_PP0 ||
> + channel == MALI_PROFILING_EVENT_CHANNEL_PP1 ||
> + channel == MALI_PROFILING_EVENT_CHANNEL_PP2 ||
> + channel == MALI_PROFILING_EVENT_CHANNEL_PP3 ||
> + channel == MALI_PROFILING_EVENT_CHANNEL_PP4 ||
> + channel == MALI_PROFILING_EVENT_CHANNEL_PP5 ||
> + channel == MALI_PROFILING_EVENT_CHANNEL_PP6);
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +is_gp_or_pp_channel(int channel)
> +{
> + return (is_gp_channel(channel) || is_pp_channel(channel));
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +is_known_channel(int channel)
> +{
> + return (is_gp_or_pp_channel(channel) ||
> + channel == MALI_PROFILING_EVENT_CHANNEL_SOFTWARE ||
> + channel == MALI_PROFILING_EVENT_CHANNEL_GPU);
> +}
> +
> +/* ************************************************************************ */
> +
> +static const char *
> +get_gp_name(int channel)
> +{
> + switch (channel) {
> + case MALI_PROFILING_EVENT_CHANNEL_GP0:
> + return "GP0";
> + default:
> + return "NOT A GP UNIT";
> + }
> +}
> +
> +/* ************************************************************************ */
> +
> +static const char *
> +get_pp_name(int channel)
> +{
> + switch (channel) {
> + case MALI_PROFILING_EVENT_CHANNEL_PP0:
> + return "PP0";
> + case MALI_PROFILING_EVENT_CHANNEL_PP1:
> + return "PP1";
> + case MALI_PROFILING_EVENT_CHANNEL_PP2:
> + return "PP2";
> + case MALI_PROFILING_EVENT_CHANNEL_PP3:
> + return "PP3";
> + case MALI_PROFILING_EVENT_CHANNEL_PP4:
> + return "PP4";
> + case MALI_PROFILING_EVENT_CHANNEL_PP5:
> + return "PP5";
> + case MALI_PROFILING_EVENT_CHANNEL_PP6:
> + return "PP6";
> + case MALI_PROFILING_EVENT_CHANNEL_PP7:
> + return "PP7";
> + default:
> + return "NOT A PP UNIT";
> + }
> +}
> +
> +/* ************************************************************************ */
> +
> +static const char *
> +get_channel_name(int channel)
> +{
> + if (is_pp_channel(channel))
> + return get_pp_name(channel);
> + else if (is_gp_channel(channel))
> + return get_gp_name(channel);
> + else if (channel == MALI_PROFILING_EVENT_CHANNEL_SOFTWARE)
> + return "Software";
> + else if (channel == MALI_PROFILING_EVENT_CHANNEL_GPU)
> + return "GPU";
> + else {
> + static char buf[64];
> +
> + snprintf(buf, sizeof(buf), "UNKNOWN(%u)", channel);
> + return buf;
> + }
> +}
> +
> +/* ************************************************************************ */
> +
> +static int
> +print_date(debug_level_t level, const mali_event *ev)
> +{
> + time_t t;
> + char str[256], stz[32];
> + struct tm *ltm;
> +
> + if (level > debug_level)
> + return 0;
> +
> + t = ev->timestamp / TIMESTAMP_FACTOR;
> + ltm = localtime(&t);
> +
> + strftime(str, sizeof(str), "%F %T", ltm);
> + strftime(stz, sizeof(stz), "%z", ltm);
> +
> + /* No newline at end */
> + return dprintf(level, "%s.%010u %s: ", str,
> + (unsigned int)(ev->timestamp % TIMESTAMP_FACTOR), stz);
> +}
*> +
> +/* ************************************************************************ */
> +
> +/* Channel is a GP channel here */
> +static bool
> +process_gp_job(mali_ctx *ctx, const mali_event *ev, int un)
> +{
> + double freq, volt, duration, power;
> + process_info *proc;
> + pid_t pid;
> +
> + if (ctx->gpu_state.freq == -1) {
> + dprintf(WARN, "GPU Frequency is unknown for finished job. "
> + "Set it to 440 MHz.\n");
> + ctx->gpu_state.freq = 440000000;
> + }
> +
> + if (ctx->gpu_state.volt == -1) {
> + dprintf(WARN, "GPU Voltage is unknown for finished job. "
> + "Set it to 1.075 V.\n");
> + ctx->gpu_state.volt = 1075000;
> + }
> +
> + freq = ctx->gpu_state.freq / 1000000.;
> + volt = ctx->gpu_state.volt / 1000000.;
> + pid = ctx->gpu_state.gp[un].pid;
> + ctx->gpu_state.gp[un].status = UNIT_FREE;
> + duration = (double)(ev->timestamp - ctx->gpu_state.gp[un].job_start) /
> + (double)TIMESTAMP_FACTOR;
> + /* Consumed power approximately can be computed as this:
> + * P = C * V^2 * f, where C is capacitance, V is voltage
> + * and f is frequency. We don't need exact Watts so we consider
> + * that 1 PW (PseudoWatt) is consumed running one GPU unit
> + * (GPx or PPx) on 266 MHz and 1 Volt. */
> + power = freq * volt * volt * duration / 266.;
> + dprintf(TASK_EVENTS, "Job was ran on GP%d by pid %d, tid %d "
> + "for %lg seconds with GPU frequency = %lg MHz, "
> + "voltage = %.03f, "
> + "consumed power = %lg PseudoWatts\n",
> + un, pid, ctx->gpu_state.gp[un].tid, duration, freq, volt,
> + power);
> +
> + proc = get_proc_info(pid);
> + if (!proc)
> + add_unknown_proc(pid, power);
> + else
> + proc->power += power;
> +
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +/* Channel is a PP channel here */
> +static bool
> +process_pp_job(mali_ctx *ctx, const mali_event *ev, int un)
> +{
> + double freq, volt, duration, power;
> + process_info *proc;
> + pid_t pid;
> +
> + if (ctx->gpu_state.freq == -1) {
> + dprintf(WARN, "GPU Frequency is unknown for finished job. "
> + "Set it to 440 MHz.\n");
> + ctx->gpu_state.freq = 440000000;
> + }
> +
> + if (ctx->gpu_state.volt == -1) {
> + dprintf(WARN, "GPU Voltage is unknown for finished job. "
> + "Set it to 1.075 V.\n");
> + ctx->gpu_state.volt = 1075000;
> + }
> +
> + freq = ctx->gpu_state.freq / 1000000.;
> + volt = ctx->gpu_state.volt / 1000000.;
> + pid = ctx->gpu_state.pp[un].pid;
> + ctx->gpu_state.pp[un].status = UNIT_FREE;
> + duration = (double)(ev->timestamp - ctx->gpu_state.pp[un].job_start) /
> + (double)TIMESTAMP_FACTOR;
> + /* See comment in process_gp_job() above. */
> + power = freq * volt * volt * duration / 266.;
> + dprintf(TASK_EVENTS, "Job was ran on PP%d by pid %d, tid %d "
> + "for %lg seconds with GPU frequency = %g MHz, "
> + "voltage = %.03f, "
> + "consumed power = %lg PseudoWatts\n",
> + un, pid, ctx->gpu_state.pp[un].tid, duration, freq, volt,
> + power);
> +
> + proc = get_proc_info(pid);
> + if (!proc)
> + add_unknown_proc(pid, power);
> + else
> + proc->power += power;
> +
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +/* Channel is a GP channel here */
> +static bool
> +process_gp_job_start(mali_ctx *ctx, const mali_event *ev)
> +{
> + int un;
> +
> + print_date(TASK_EVENTS, ev);
> + dprintf(TASK_EVENTS, "GP job started at %s by pid = %u, tid = %u\n",
> + get_gp_name(ev->channel), ev->d0, ev->d1);
> + un = gp_number(ev->channel);
> + if (ctx->gpu_state.gp[un].status != UNIT_FREE) {
> + edprintf(WARN, "We have missed GP Job Stop event. "
> + "Considering running task finished.\n");
> + if (!process_gp_job(ctx, ev, un))
> + return false;
> + }
> +
> + ctx->gpu_state.gp[un].status = UNIT_BUSY;
> + ctx->gpu_state.gp[un].job_start = ev->timestamp;
> + ctx->gpu_state.gp[un].pid = ev->d0;
> + ctx->gpu_state.gp[un].tid = ev->d1;
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +/* Channel is a PP channel here */
> +static bool
> +process_pp_job_start(mali_ctx *ctx, const mali_event *ev)
> +{
> + int un;
> +
> + print_date(TASK_EVENTS, ev);
> + dprintf(TASK_EVENTS, "PP job started at %s by pid = %u, tid = %u\n",
> + get_pp_name(ev->channel), ev->d0, ev->d1);
> + un = pp_number(ev->channel);
> + if (ctx->gpu_state.pp[un].status != UNIT_FREE) {
> + edprintf(WARN, "We have missed PP Job Stop event. "
> + "Considering running task finished.\n");
> + if (!process_pp_job(ctx, ev, un))
> + return false;
> + }
> +
> + ctx->gpu_state.pp[un].status = UNIT_BUSY;
> + ctx->gpu_state.pp[un].job_start = ev->timestamp;
> + ctx->gpu_state.pp[un].pid = ev->d0;
> + ctx->gpu_state.pp[un].tid = ev->d1;
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +process_start_event(mali_ctx *ctx, const mali_event *ev)
> +{
> + /* Skip events that was before program start
> + * or after program finish */
> + if ((signed long long)ev->timestamp < ctx->start_time ||
> + (signed long long)ev->timestamp > ctx->finish_time) {
> + dprintf(MISC_EVENTS,
> + "Skipping Start event out of time interval (ts=%lld, "
> + "st=%lld, fn=%lld).\n",
> + ev->timestamp, ctx->start_time, ctx->finish_time);
> + return true;
> + }
> +
> + if (!is_gp_or_pp_channel(ev->channel)) {
> + print_date(MISC_EVENTS, ev);
> + dprintf(MISC_EVENTS, "Skipping Start event from uninterested "
> + "channel %s.\n", get_channel_name(ev->channel));
> + return true;
> + }
> +
> + if (is_gp_channel(ev->channel))
> + return process_gp_job_start(ctx, ev);
> + else
> + return process_pp_job_start(ctx, ev);
> +}
> +
> +/* ************************************************************************ */
> +
> +/* Channel is a GP channel here */
> +static bool
> +process_gp_job_stop(mali_ctx *ctx, const mali_event *ev)
> +{
> + int un;
> + int src0, src1;
> +
> + src0 = ev->d2 & 0xff;
> + src1 = ev->d2 >> 8;
> + print_date(TASK_EVENTS, ev);
> + dprintf(TASK_EVENTS,
> + "GP job stopped at %s with val0 = %u, val1 = %u, "
> + "src0 = %u, src1 = %u\n",
> + get_gp_name(ev->channel), ev->d0, ev->d1, src0, src1);
> + un = gp_number(ev->channel);
> + if (ctx->gpu_state.gp[un].status != UNIT_BUSY) {
> + edprintf(WARN, "Skipping GP Job Stop event because unit "
> + "is already free.\n");
> + return true;
> + }
> +
> + return process_gp_job(ctx, ev, un);
> +}
> +
> +/* ************************************************************************ */
> +
> +/* Channel is a PP channel here */
> +static bool
> +process_pp_job_stop(mali_ctx *ctx, const mali_event *ev)
> +{
> + int un;
> + int src0, src1;
> +
> + src0 = ev->d2 & 0xff;
> + src1 = ev->d2 >> 8;
> + print_date(TASK_EVENTS, ev);
> + dprintf(TASK_EVENTS,
> + "PP job stopped at %s with val0 = %u, val1 = %u, "
> + "src0 = %u, src1 = %u\n",
> + get_pp_name(ev->channel), ev->d0, ev->d1, src0, src1);
> + un = pp_number(ev->channel);
> + if (ctx->gpu_state.pp[un].status != UNIT_BUSY) {
> + edprintf(WARN, "Skipping PP Job Stop event because unit "
> + "is already free.\n");
> + return true;
> + }
> +
> + return process_pp_job(ctx, ev, un);
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +process_stop_event(mali_ctx *ctx, const mali_event *ev)
> +{
> + /* Skip events that was before program start
> + * or after program finish */
> + if ((signed long long)ev->timestamp < ctx->start_time ||
> + (signed long long)ev->timestamp > ctx->finish_time) {
> + dprintf(MISC_EVENTS,
> + "Skipping Stop event out of time interval.\n");
> + return true;
> + }
> +
> + if (!is_gp_or_pp_channel(ev->channel)) {
> + print_date(MISC_EVENTS, ev);
> + dprintf(MISC_EVENTS,
> + "Skipping Stop event from uninterested channel %s.\n",
> + get_channel_name(ev->channel));
> + return true;
> + }
> +
> + if (is_gp_channel(ev->channel))
> + return process_gp_job_stop(ctx, ev);
> + else
> + return process_pp_job_stop(ctx, ev);
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +process_sw_global_try_lock(const mali_event *ev)
> +{
> + print_date(MISC_EVENTS, ev);
> + dprintf(MISC_EVENTS,
> + "SW Global Try Lock by pid = %u, tid = %u, key = %u\n",
> + ev->d0, ev->d1, ev->d2);
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +process_sw_global_lock(const mali_event *ev)
> +{
> + print_date(MISC_EVENTS, ev);
> + dprintf(MISC_EVENTS,
> + "SW Global Lock by pid = %u, tid = %u, key = %u\n",
> + ev->d0, ev->d1, ev->d2);
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +process_sw_global_unlock(const mali_event *ev)
> +{
> + print_date(MISC_EVENTS, ev);
> + dprintf(MISC_EVENTS,
> + "SW Global Unlock by pid = %u, tid = %u, key = %u\n",
> + ev->d0, ev->d1, ev->d2);
> + return true;
> +}
> +
can we factor out generic
process_sw_global_lock_op(const mali_event *ev, const char *op)
{
print_date(MISC_EVENTS, ev);
dprintf(MISC_EVENTS,
"%s by pid = %u, tid = %u, key = %u\n", op,
ev->d0, ev->d1, ev->d2);
return true;
}
and use
process_sw_global_lock_op(ev, SW_GLOBAL_TRY_LOCK);
process_sw_global_lock_op(ev, SW_GLOBAL_LOCK);
process_sw_global_lock_op(ev, SW_GLOBAL_UNLOCK);
where #define SW_GLOBAL_TRY_LOCK "SW Global Try Lock", etc.
for example,
process_pp_job() and process_pp_job() are identical, with the only difference in
unit_state being addressed: pp or gp.
in other words, if there is no difference from techical point of view (well, almost)
what job we start/stop/process, then *PROBABLY* we can have generic job start/stop/process
function taking UNIT_STATE_TYPE (e.g.) as additional parameter.
what do you think?
-ss
> +/* ************************************************************************ */
> +
> +/* on:
> + * 0 -- Device is powering up,
> + * 1 -- Device is powering down,
> + * -1 -- Don't care about device state and don't issue warnings. */
> +static void
> +split_gp_jobs(mali_ctx *ctx, const mali_event *ev, int on)
> +{
> + int i;
> +
> + for (i = 0; i < GP_NUM; i++) {
> + if (ctx->gpu_state.gp[i].status == UNIT_FREE)
> + continue;
> +
> + if (on == 1)
> + dprintf(WARN,
> + "Strange thing. GP%d is turned on while "
> + "task is already running.\n", i);
> + else if (on == 0)
> + dprintf(WARN,
> + "Strange thing. GP%d is turned off while "
> + "task is still running.\n", i);
> +
> + dprintf(MISC_EVENTS, "Next GP job is splitted.\n");
> + /* Split task and "restart" job with the same pid */
> + process_gp_job(ctx, ev, i);
> + ctx->gpu_state.gp[i].status = UNIT_BUSY;
> + ctx->gpu_state.gp[i].job_start = ev->timestamp;
> + }
> +}
> +
> +/* ************************************************************************ */
> +
> +static void
> +split_pp_jobs(mali_ctx *ctx, const mali_event *ev, int on)
> +{
> + int i;
> +
> + for (i = 0; i < PP_NUM; i++) {
> + if (ctx->gpu_state.pp[i].status == UNIT_FREE)
> + continue;
> +
> + if (on == 1)
> + dprintf(WARN,
> + "Strange thing. PP%d is turned on while "
> + "task is already running.\n", i);
> + else if (on == 0)
> + dprintf(WARN,
> + "Strange thing. PP%d is turned off while "
> + "task is still running.\n", i);
> +
> + dprintf(MISC_EVENTS, "Next PP job is splitted.\n");
> + /* Split task and "restart" job with the same pid */
> + process_pp_job(ctx, ev, i);
> + ctx->gpu_state.pp[i].status = UNIT_BUSY;
> + ctx->gpu_state.pp[i].job_start = ev->timestamp;
> + }
> +}
> +
> +/* ************************************************************************ */
> +
> +static void
> +split_jobs(mali_ctx *ctx, const mali_event *ev, int on)
> +{
> + /* It seems that it is impossible to change Only voltage without
> + * changing frequency. And it seems that these events are always
> + * come in pairs. So we split our jobs at first event and skip
> + * second to avoid reports with strange frequency / voltage
> + * combination. It would be better to know dependancy between
> + * frequency and voltage so we can adjust another when one changes.*/
> + if (ctx->gpu_state.split_count & 1)
> + return;
> +
> + split_gp_jobs(ctx, ev, on);
> + split_pp_jobs(ctx, ev, on);
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +process_sw_gpu_freq(mali_ctx *ctx, const mali_event *ev)
> +{
> + if (!ev->d1) {
> + print_date(MISC_EVENTS, ev);
> + dprintf(MISC_EVENTS, "SW GPU Freq rate (previous) = %u\n",
> + ev->d0);
> + return true;
> + }
> +
> + if (ctx->gpu_state.freq == (signed int)ev->d0)
> + return true;
> +
> + split_jobs(ctx, ev, -1);
> + ctx->gpu_state.freq = ev->d0;
> + ctx->gpu_state.split_count++;
> + print_date(TASK_EVENTS, ev);
> + dprintf(TASK_EVENTS, "GPU frequency changed to %d\n",
> + ctx->gpu_state.freq);
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +process_sw_gpu_volts(mali_ctx *ctx, const mali_event *ev)
> +{
> + if (ev->d2 != 2) {
> + print_date(MISC_EVENTS, ev);
> + dprintf(MISC_EVENTS,
> + "SW GPU Volts min_uV = %u, max_uV = %u (Desired)\n",
> + ev->d0, ev->d1);
> + return true;
> + }
> +
> + if (ctx->gpu_state.volt == (signed int)ev->d0)
> + return true;
> +
> + split_jobs(ctx, ev, -1);
> + ctx->gpu_state.volt = ev->d0;
> + ctx->gpu_state.split_count++;
> + print_date(TASK_EVENTS, ev);
> + dprintf(TASK_EVENTS, "GPU voltage changed to %d\n",
> + ctx->gpu_state.volt);
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +process_single_software_event(mali_ctx *ctx, const mali_event *ev)
> +{
> + switch (ev->data) {
> + case MALI_PROFILING_EVENT_REASON_SINGLE_SW_GPU_FREQ:
> + return process_sw_gpu_freq(ctx, ev);
> + case MALI_PROFILING_EVENT_REASON_SINGLE_SW_GPU_VOLTS:
> + return process_sw_gpu_volts(ctx, ev);
> + case MALI_PROFILING_EVENT_REASON_SINGLE_SW_GLOBAL_TRY_LOCK:
> + return process_sw_global_try_lock(ev);
> + case MALI_PROFILING_EVENT_REASON_SINGLE_SW_GLOBAL_LOCK:
> + return process_sw_global_lock(ev);
> + case MALI_PROFILING_EVENT_REASON_SINGLE_SW_GLOBAL_UNLOCK:
> + return process_sw_global_unlock(ev);
> + break;
> +/* case MALI_PROFILING_EVENT_REASON_SINGLE_SW_VBLANK_VSYNC:*/
> +/* case MALI_PROFILING_EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE:*/
> + default:
> + print_date(WARN, ev);
> + dprintf(WARN, "Skipping Single software event "
> + "with unknown data %d.\n", ev->data);
> + break;
> + }
> +
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +process_hw_interrupt(const mali_event *ev)
> +{
> + if (!is_gp_or_pp_channel(ev->channel)) {
> + print_date(WARN, ev);
> + dprintf(WARN, "Skipping HW Interrupt event from wrong "
> + "channel %s.\n", get_channel_name(ev->channel));
> + return true;
> + }
> +
> + print_date(MISC_EVENTS, ev);
> + dprintf(MISC_EVENTS, "HW Interrupt at %s with irq_readout = %u\n",
> + get_channel_name(ev->channel), ev->d0);
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +process_hw_flush(const mali_event *ev)
> +{
> + if (!is_gp_or_pp_channel(ev->channel)) {
> + print_date(WARN, ev);
> + dprintf(WARN, "Skipping HW Flush event from wrong "
> + "channel %s.\n", get_channel_name(ev->channel));
> + return true;
> + }
> +
> + print_date(MISC_EVENTS, ev);
> + dprintf(MISC_EVENTS,
> + "HW Flush at %s with frame_builder_id = %u, flush_id, = %u\n",
> + get_channel_name(ev->channel), ev->d0, ev->d1);
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +update_jobs(mali_ctx *ctx, const mali_event *ev, int new_freq, int new_volt)
> +{
> + /* Nothing to change */
> + if (ctx->gpu_state.freq == new_freq &&
> + ctx->gpu_state.volt == new_volt)
> + return true;
> +
> + if (new_freq) {
> + dprintf(TASK_EVENTS, "GPU is ON now\n");
> + /* Shouldn't have any jobs running but recheck it for sure */
> + split_jobs(ctx, ev, 1);
> + } else {
> + dprintf(TASK_EVENTS, "GPU is OFF now\n");
> + /* Shouldn't have any jobs running but recheck it for sure */
> + split_jobs(ctx, ev, 0);
> + }
> +
> + ctx->gpu_state.freq = new_freq;
> + ctx->gpu_state.volt = new_volt;
> + ctx->gpu_state.split_count = 0;
> + dprintf(TASK_EVENTS, "GPU frequency changed to %d, voltage to %d\n",
> + ctx->gpu_state.freq, ctx->gpu_state.volt);
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +process_gpu_freq_volt_change(mali_ctx *ctx, const mali_event *ev)
> +{
> + if (ev->channel != MALI_PROFILING_EVENT_CHANNEL_GPU) {
> + print_date(WARN, ev);
> + dprintf(WARN,
> + "Skipping GPU Freq Volt event from wrong channel "
> + "%s.\n", get_channel_name(ev->channel));
> + return true;
> + }
> +
> + dprintf(MISC_EVENTS, "GPU Volt Change with mali_gpu_clk = %u, "
> + "mali_gpu_vol / 1000 = %u\n",
> + ev->d0, ev->d1);
> + return update_jobs(ctx, ev, ev->d0 * 1000000, ev->d1 * 1000);
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +process_single_hardware_event(mali_ctx *ctx, const mali_event *ev)
> +{
> + switch (ev->data) {
> + case MALI_PROFILING_EVENT_REASON_SINGLE_HW_INTERRUPT:
> + return process_hw_interrupt(ev);
> + case MALI_PROFILING_EVENT_REASON_SINGLE_HW_FLUSH:
> + return process_hw_flush(ev);
> + case MALI_PROFILING_EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE:
> + return process_gpu_freq_volt_change(ctx, ev);
> + default:
> + print_date(WARN, ev);
> + dprintf(WARN, "Skipping Single hardware event "
> + "with unknown data %d.\n", ev->data);
> + break;
> + }
> +
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +process_single_event(mali_ctx *ctx, const mali_event *ev)
> +{
> + if (!is_known_channel(ev->channel)) {
> + print_date(WARN, ev);
> + dprintf(WARN,
> + "Skipping Single event with unknown channel %d.\n",
> + ev->channel);
> + return true;
> + }
> +
> + if (ev->channel == MALI_PROFILING_EVENT_CHANNEL_SOFTWARE)
> + return process_single_software_event(ctx, ev);
> + else
> + return process_single_hardware_event(ctx, ev);
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +process_unknown_event(const mali_event *ev)
> +{
> + print_date(WARN, ev);
> + dprintf(WARN,
> + "Skipping unknown event: type = %u, channel = %u, data = %u, "
> + "d0 = %u, d1 = %u, d2 = %u, d3 = %u, d4 = %u.\n",
> + ev->type, ev->channel, ev->data, ev->d0, ev->d1, ev->d2,
> + ev->d3, ev->d4);
> + return true;
> +}
> +
> +
> +/* ************************************************************************ */
> +
> +static bool
> +process_mali_event(mali_ctx *ctx, const mali_event *ev)
> +{
> + switch (ev->type) {
> + case MALI_PROFILING_EVENT_TYPE_SINGLE:
> + return process_single_event(ctx, ev);
> + case MALI_PROFILING_EVENT_TYPE_START:
> + return process_start_event(ctx, ev);
> + case MALI_PROFILING_EVENT_TYPE_STOP:
> + return process_stop_event(ctx, ev);
> + case MALI_PROFILING_EVENT_TYPE_SUSPEND:
> + print_date(MISC_EVENTS, ev);
> + dprintf(MISC_EVENTS, "Skipping Suspend event.\n");
> + break;
> + case MALI_PROFILING_EVENT_TYPE_RESUME:
> + print_date(MISC_EVENTS, ev);
> + dprintf(MISC_EVENTS, "Skipping Resume event.\n");
> + break;
> + default:
> + return process_unknown_event(ev);
> + }
> +
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +parse_mali_event(mali_ctx *ctx, const char *line)
> +{
> + mali_event ev;
> + unsigned int event_id;
> +
> + if (sscanf(line, "%llu %u %u %u %u %u %u", &ev.timestamp, &event_id,
> + &ev.d0, &ev.d1, &ev.d2, &ev.d3, &ev.d4) != 7) {
> + edprintf(CRIT, "Wrong event line: %s", line);
> + return false;
> + }
> +
> + /* Doesn't skip events here because we need information about current
> + * voltage and frequency. Will skip only job-related events later. */
> +
> + ev.type = (event_id >> 24) & 0xf;
> + ev.channel = (event_id >> 16) & 0xff;
> + ev.data = event_id & 0xffff;
> + return process_mali_event(ctx, &ev);
> +}
> +
> +/* ************************************************************************ */
> +
> +bool
> +process_mali_events(mali_ctx *ctx)
> +{
> + FILE *f;
> + char line[4096];
> +
> + if (!ctx || ctx->processed)
> + return false;
> +
> + f = fopen(ctx->fname, "r");
> + if (!f) {
> + edprintf(CRIT, "Can't open \"%s\".\n", ctx->fname);
> + ctx->error_flag = 1;
> + return false;
> + }
> +
> + while (fgets(line, sizeof(line), f))
> + if (parse_mali_event(ctx, line) == -1) {
> + ctx->error_flag = 1;
> + return false;
> + }
> +
> + fclose(f);
> + ctx->processed = true;
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +static void
> +init_gpu_state(mali_ctx *ctx)
> +{
> + memset(&ctx->gpu_state, '\0', sizeof(mali_state));
> + ctx->gpu_state.freq = -1; /* Unknown */
> + ctx->gpu_state.volt = -1; /* Unknown */
> +}
> +
> +/* ************************************************************************ */
> +
> +bool
> +cleanup_mali_capture(mali_ctx **ctx)
> +{
> + if (!ctx || !*ctx)
> + return true;
> +
> + clear_procs_info();
> +
> + if ((*ctx)->live &&
> + !restore_mode((*ctx)->old_enable, (*ctx)->old_record))
> + return false;
> +
> + free(*ctx);
> + *ctx = NULL;
> +
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +static bool
> +stat_callback(const process_info *pinfo, void *data UNUSED)
> +{
> + const char *comm, *cmdline;
> +
> + if (pinfo->power == 0) /* Skip empty */
> + return true;
> +
> + dprintf(TOTAL_REPORT,
> + "Total consumed power by pid %d is %lg PseudoWatts*s\n",
> + pinfo->pid, pinfo->power);
> +
> + if (!pinfo->unknown_name) {
> + comm = pinfo->comm;
> + cmdline = pinfo->cmdline;
> + } else if (!get_pt_proc_info(pinfo->pid, &comm, &cmdline)) {
> + comm = "(Unknown)";
> + cmdline = "(Unknown)";
> + }
> +
> + report.begin_row(ROW_SOFTWARE);
> + report.begin_cell();
> + report.addf("%.4f PWs", pinfo->power);
> + report.begin_cell();
> + report.addf("%d", pinfo->pid);
> + report.begin_cell();
> + report.add(comm);
> + report.begin_cell();
> + report.add(cmdline);
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +bool
> +print_mali_stat(mali_ctx *ctx)
> +{
> + char *oldlocale;
> +
> + if (!ctx)
> + return false;
> +
> + if (ctx->error_flag || ctx->started || !ctx->processed) {
> + edprintf(CRIT,
> + "Errors was encountered during processing '%s'\n",
> + ctx->fname);
> + return false;
> + }
> +
> + oldlocale = setlocale(LC_NUMERIC, NULL);
> + oldlocale = strdup(oldlocale);
> + setlocale(LC_NUMERIC, "C"); /* For dots in %f in printf() */
> +
> + report.add_header("Overview of MALI GPU power consumers");
> + report.begin_table(TABLE_WIDE);
> + report.begin_row();
> + report.begin_cell(CELL_SOFTWARE_PROCESS);
> + report.add("Power est. (PseudoWatts*s)");
> + report.begin_cell(CELL_SOFTWARE_DESCRIPTION);
> + report.add("PID");
> + report.begin_cell(CELL_SOFTWARE_DESCRIPTION);
> + report.add("Name");
> + report.begin_cell(CELL_SOFTWARE_DESCRIPTION);
> + report.add("Description");
> + print_sorted_procs(stat_callback, NULL);
> +
> + setlocale(LC_NUMERIC, oldlocale);
> + free(oldlocale);
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +bool
> +stop_mali_capture(mali_ctx *ctx)
> +{
> + if (!ctx || !ctx->started)
> + return false;
> +
> + if (ctx->live) {
> + struct timeval tv;
> +
> + if (!set_record(0)) /* Stop events recording */
> + return false;
> +
> + gettimeofday(&tv, NULL);
> + ctx->finish_time = (tv.tv_sec * 1000000ll + tv.tv_usec) *
> + (TIMESTAMP_FACTOR / 1000000ll);
> + }
> +
> + ctx->started = false;
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +bool
> +start_mali_capture(mali_ctx *ctx)
> +{
> + if (!ctx || ctx->started)
> + return false;
> +
> + clear_procs_info();
> + ctx->processed = false;
> + ctx->error_flag = false;
> + init_gpu_state(ctx);
> +
> + if (ctx->live) {
> + struct timeval tv;
> +
> + gettimeofday(&tv, NULL);
> + /* Could be wrong if TIMESTAMP_FACTOR is not divisible
> + * by 1000000 or if TIMESTAMP_FACTOR is less than 1000000. */
> + ctx->start_time = (tv.tv_sec * 1000000ll + tv.tv_usec) *
> + (TIMESTAMP_FACTOR / 1000000ll);
> +
> + if (!set_mode()) /* Start events recording */
> + return false;
> + } else {
> + ctx->start_time = -1;
> + ctx->finish_time = 0x7fffffffffffffffll;
> + }
> +
> + ctx->started = true;
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +mali_ctx *
> +init_mali_capture_ex(bool live, const char *fname)
> +{
> + mali_ctx *ctx;
> +
> + ctx = (mali_ctx *)malloc(sizeof(mali_ctx));
> + if (!ctx) {
> + edprintf(CRIT, "No free memory.\n");
> + return NULL;
> + }
> +
> + memset(ctx, '\0', sizeof(mali_ctx));
> + ctx->live = live;
> + ctx->fname = fname;
> + ctx->started = false;
> + ctx->processed = false;
> +
> + if (live && !save_mode(&ctx->old_enable, &ctx->old_record)) {
> + free(ctx);
> + return NULL;
> + }
> +
> + return ctx;
> +}
> +
> +/* ************************************************************************ */
> +
> +mali_ctx *
> +init_mali_capture()
> +{
> + return init_mali_capture_ex(true, MALI_PROFILING_EVENTS);
> +}
> +
> +/* ************************************************************************ */
> +
> +#else /* !ARM */
> +
> +/* ************************************************************************ */
> +
> +mali_ctx *
> +init_mali_capture()
> +{
> + return NULL;
> +}
> +
> +/* ************************************************************************ */
> +
> +bool
> +start_mali_capture(mali_ctx *ctx UNUSED)
> +{
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +bool
> +stop_mali_capture(mali_ctx *ctx UNUSED)
> +{
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +bool
> +process_mali_events(mali_ctx *ctx UNUSED)
> +{
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +bool
> +print_mali_stat(mali_ctx *ctx UNUSED)
> +{
> + return true;
> +}
> +
> +/* ************************************************************************ */
> +
> +bool
> +cleanup_mali_capture(mali_ctx **ctx UNUSED)
> +{
> + return true;
> +}
> +
> +#endif /* !ARM */
> --
> 1.7.5.4
>
> _______________________________________________
> PowerTop mailing list
> PowerTop(a)lists.01.org
> https://lists.01.org/mailman/listinfo/powertop
>
^ permalink raw reply [flat|nested] 2+ messages in thread
* [Powertop] [FYI][RFC][PATCH 2/2] MALI GPU profiling support
@ 2012-11-01 12:00 Igor Zhbanov
0 siblings, 0 replies; 2+ messages in thread
From: Igor Zhbanov @ 2012-11-01 12:00 UTC (permalink / raw)
To: powertop
[-- Attachment #1: Type: text/plain, Size: 43070 bytes --]
This patch adds MALI GPU profiling support.
The information is collected from internal tracing mechanism
(/sys/kernel/debug/mali/profiling/).
Currently the collected data is written in reports only and not
on screen in interactive mode.
Reviewed-by: Kyungmin Park <kyungmin.park(a)samsung.com>
---
src/Makefile.am | 1 +
src/main.cpp | 8 +
src/mali-internal-events/mali-events.h | 143 +++
src/mali-internal-events/mali-internal-events.cpp | 1279 +++++++++++++++++++++
4 files changed, 1431 insertions(+), 0 deletions(-)
create mode 100644 src/mali-internal-events/mali-events.h
create mode 100644 src/mali-internal-events/mali-internal-events.cpp
diff --git a/src/Makefile.am b/src/Makefile.am
index e3eb6c2..af98867 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -28,6 +28,7 @@ powertop_SOURCES = parameters/persistent.cpp parameters/learn.cpp parameters/par
tuning/tuningsysfs.cpp tuning/wifi.h tuning/runtime.cpp tuning/tunable.h \
tuning/runtime.h tuning/tuningusb.h tuning/iw.h calibrate/calibrate.cpp \
calibrate/calibrate.h measurement/measurement.cpp measurement/power_supply.cpp \
+ mali-internal-events/mali-events.h mali-internal-events/mali-internal-events.cpp \
mali-internal-events/proc-scan.cpp mali-internal-events/proc-scan.h \
measurement/measurement.h measurement/acpi.cpp measurement/sysfs.h measurement/sysfs.cpp \
measurement/acpi.h measurement/extech.cpp measurement/power_supply.h measurement/extech.h \
diff --git a/src/main.cpp b/src/main.cpp
index 83adfad..c901ce7 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -61,6 +61,7 @@
#define DEBUGFS_MAGIC 0x64626720
#include "mali-internal-events/proc-scan.h"
+#include "mali-internal-events/mali-events.h"
int debug_learning = 0;
unsigned time_out = 20;
@@ -183,6 +184,7 @@ static void do_sleep(int seconds)
} while (1);
}
+mali_ctx *ctx = NULL;
void one_measurement(int seconds, char *workload)
{
@@ -191,6 +193,7 @@ void one_measurement(int seconds, char *workload)
devices_start_measurement();
start_process_measurement();
start_cpu_measurement();
+ start_mali_capture(ctx);
if (workload && workload[0]) {
scanproc(0, 0);
@@ -206,9 +209,11 @@ void one_measurement(int seconds, char *workload)
collect_open_devices();
devices_end_measurement();
end_power_measurement();
+ stop_mali_capture(ctx);
process_cpu_data();
process_process_data();
+ process_mali_events(ctx);
/* output stats */
process_update_display();
@@ -257,11 +262,14 @@ void make_report(int time, char *workload, int iterations, char *file)
for (int i=0; i != iterations; i++){
init_report_output(file, iterations);
initialize_tuning();
+ ctx = init_mali_capture();
/* and then the real measurement */
one_measurement(time, workload);
report_show_tunables();
+ print_mali_stat(ctx);
finish_report_output();
clear_tuning();
+ cleanup_mali_capture(&ctx);
}
/* and wrap up */
learn_parameters(50, 0);
diff --git a/src/mali-internal-events/mali-events.h b/src/mali-internal-events/mali-events.h
new file mode 100644
index 0000000..825f926
--- /dev/null
+++ b/src/mali-internal-events/mali-events.h
@@ -0,0 +1,143 @@
+/* Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ * or just google for it.
+ *
+ * MALI GPU internal events tracer for r3p0 driver version.
+ * Written by Igor Zhbanov <i.zhbanov(a)samsung.com>,
+ * Alina Litvinova <alina.litvinova(a)gmail.com>
+ * 2012.06 */
+
+#ifndef _MALI_EVENTS_H_
+#define _MALI_EVENTS_H_
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+struct mali_event {
+ unsigned long long timestamp;
+ unsigned int type, channel, data, d0, d1, d2, d3, d4;
+};
+
+/*
+Event ID bit format:
+ 3 2 1
+10987654321098765432109876543210
+RRRRTTTTCCCCCCCCDDDDDDDDDDDDDDDD
+
+RRRR -- reserved for future use.
+
+TTTT -- type of the event: */
+#define MALI_PROFILING_EVENT_TYPE_SINGLE 0
+#define MALI_PROFILING_EVENT_TYPE_START 1
+#define MALI_PROFILING_EVENT_TYPE_STOP 2
+#define MALI_PROFILING_EVENT_TYPE_SUSPEND 3
+#define MALI_PROFILING_EVENT_TYPE_RESUME 4
+
+/*
+CCCCCCCC -- channel/source of the event: */
+#define MALI_PROFILING_EVENT_CHANNEL_SOFTWARE 0
+#define MALI_PROFILING_EVENT_CHANNEL_GP0 1
+#define MALI_PROFILING_EVENT_CHANNEL_PP0 5
+#define MALI_PROFILING_EVENT_CHANNEL_PP1 6
+#define MALI_PROFILING_EVENT_CHANNEL_PP2 7
+#define MALI_PROFILING_EVENT_CHANNEL_PP3 8
+#define MALI_PROFILING_EVENT_CHANNEL_PP4 9
+#define MALI_PROFILING_EVENT_CHANNEL_PP5 10
+#define MALI_PROFILING_EVENT_CHANNEL_PP6 11
+#define MALI_PROFILING_EVENT_CHANNEL_PP7 12
+#define MALI_PROFILING_EVENT_CHANNEL_GPU 21
+
+#define GP_NUM 1 /* Maximal number of GP units */
+#define PP_NUM 8 /* Maximal number of PP units */
+
+/*
+DDDDDDDDDDDDDDDD -- event data.
+
+Events data for MALI_PROFILING_EVENT_TYPE_SINGLE event type
+from software channel: */
+#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_NONE 0 /* Not used */,
+#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_NEW_FRAME 1 /* Not used */,
+#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_FLUSH 2 /* Not used */,
+#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_SWAP_BUFFERS 3 /* Not used */,
+/*#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_FB_EVENT 4 // NOT USED */
+#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_GPU_FREQ 4
+#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_GPU_VOLTS 5
+#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_GLOBAL_TRY_LOCK 6
+#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_GLOBAL_LOCK 7
+#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_GLOBAL_UNLOCK 8
+#define MALI_PROFILING_EVENT_REASON_SINGLE_SW_VBLANK_VSYNC 9
+
+/*
+Events data for MALI_PROFILING_EVENT_TYPE_SINGLE event type
+from a HW channel (GPx + PPx + GPU): */
+#define MALI_PROFILING_EVENT_REASON_SINGLE_HW_NONE 0 /* Not used */
+#define MALI_PROFILING_EVENT_REASON_SINGLE_HW_INTERRUPT 1
+#define MALI_PROFILING_EVENT_REASON_SINGLE_HW_FLUSH 2
+#define MALI_PROFILING_EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE 10
+
+#define TIMESTAMP_FACTOR 1000000000ll
+
+#define MALI_PROFILING_PATH "/sys/kernel/debug/mali/profiling/"
+#define MALI_PROFILING_EVENTS MALI_PROFILING_PATH "events"
+#define MALI_PROFILING_RECORD MALI_PROFILING_PATH "record"
+#define MALI_PROFILING_ENABLE MALI_PROFILING_PATH "proc/default/enable"
+
+enum unit_status {
+ UNIT_FREE = 0,
+ UNIT_BUSY
+};
+
+struct unit_state {
+ long long job_start;
+ pid_t pid, tid;
+ unit_status status;
+};
+
+struct mali_state {
+ unit_state gp[GP_NUM];
+ unit_state pp[PP_NUM];
+ int freq, volt; /* I hope GPU frequency will not be above 2 GHz ;-) */
+ int split_count;
+};
+
+struct mali_ctx {
+ int old_enable, old_record;
+ /* For filtering events timestamp */
+ long long start_time, finish_time;
+ const char *fname;
+ mali_state gpu_state;
+ bool live, error_flag, started, processed;
+};
+
+mali_ctx *init_mali_capture();
+bool start_mali_capture(mali_ctx *ctx);
+bool stop_mali_capture(mali_ctx *ctx);
+bool process_mali_events(mali_ctx *ctx);
+bool print_mali_stat(mali_ctx *ctx);
+bool cleanup_mali_capture(mali_ctx **ctx);
+
+bool get_pt_proc_info(pid_t pid, const char **name, const char **cmdline);
+
+#ifndef UNUSED
+#define UNUSED __attribute__((unused))
+#endif /* UNUSED */
+
+#endif /* _MALI_EVENTS_H_ */
diff --git a/src/mali-internal-events/mali-internal-events.cpp b/src/mali-internal-events/mali-internal-events.cpp
new file mode 100644
index 0000000..686d1c3
--- /dev/null
+++ b/src/mali-internal-events/mali-internal-events.cpp
@@ -0,0 +1,1279 @@
+/* Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ * or just google for it.
+ *
+ * MALI GPU internal events tracer for r3p0 driver version.
+ * Written by Igor Zhbanov <i.zhbanov(a)samsung.com>,
+ * Alina Litvinova <alina.litvinova(a)gmail.com>
+ * 2012.06 */
+
+#define _POSIX_SOURCE
+
+#include <time.h>
+#include <stdio.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include "proc-scan.h"
+#include "mali-events.h"
+
+#include "../report/report.h"
+#include "../report/report-maker.h"
+
+#ifdef __arm__
+
+enum debug_level_t {
+ NO_DEBUG = 0,
+ CRIT,
+ TOTAL_REPORT,
+ WARN,
+ PER_UNIT_REPORT,
+ TASK_EVENTS,
+ MISC_EVENTS
+};
+
+static debug_level_t debug_level = CRIT;
+
+/* ************************************************************************ */
+
+static int dprintf(debug_level_t level, const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3)));
+
+static int
+dprintf(debug_level_t level, const char *fmt, ...) {
+ int ret;
+
+ if (level > debug_level)
+ return 0;
+
+ va_list ap;
+ va_start(ap, fmt);
+ ret = vprintf(fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+/* ************************************************************************ */
+
+static int edprintf(debug_level_t level, const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3)));
+
+static int
+edprintf(debug_level_t level, const char *fmt, ...) {
+ int ret;
+
+ if (level > debug_level)
+ return 0;
+
+ va_list ap;
+ va_start(ap, fmt);
+ ret = vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+/* ************************************************************************ */
+
+static int
+get_int_value(const char *fname)
+{
+ FILE *f;
+ int value;
+
+ f = fopen(fname, "r");
+ if (!f) {
+ edprintf(CRIT, "Can't open \"%s\".\n", fname);
+ return -1;
+ }
+
+ if (fscanf(f, "%d", &value) != 1) {
+ edprintf(CRIT, "Can't read \"%s\" or its value is in wrong "
+ "format.\n", fname);
+ fclose(f);
+ return -1;
+ }
+
+ fclose(f);
+ return value;
+}
+
+/* ************************************************************************ */
+
+static bool
+set_int_value(const char *fname, int value)
+{
+ FILE *f;
+
+ f = fopen(fname, "w");
+ if (!f) {
+ edprintf(CRIT, "Can't open \"%s\".\n", fname);
+ return false;
+ }
+
+ fprintf(f, "%d\n", value);
+ fclose(f);
+ return true;
+}
+
+/* ************************************************************************ */
+
+static int
+get_enable()
+{
+ return get_int_value(MALI_PROFILING_ENABLE);
+}
+
+/* ************************************************************************ */
+
+static bool
+set_enable(int value)
+{
+ return set_int_value(MALI_PROFILING_ENABLE, value);
+}
+
+/* ************************************************************************ */
+
+static int
+get_record()
+{
+ return get_int_value(MALI_PROFILING_RECORD);
+}
+
+/* ************************************************************************ */
+
+static bool
+set_record(int value)
+{
+ return set_int_value(MALI_PROFILING_RECORD, value);
+}
+
+/* ************************************************************************ */
+
+static bool
+save_mode(int *old_enable, int *old_record)
+{
+ *old_enable = get_enable();
+ if (*old_enable == -1) {
+ edprintf(CRIT, "Can't get value of \"" MALI_PROFILING_ENABLE
+ "\".\n");
+ return false;
+ }
+
+ *old_record = get_record();
+ if (*old_record == -1) {
+ edprintf(CRIT, "Can't get value of \"" MALI_PROFILING_RECORD
+ "\".\n");
+ return false;
+ }
+
+ return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+set_mode()
+{
+ if (!set_enable(1))
+ return false;
+
+ /* Enable recording. Don't clean previous records to collect
+ * voltage and frequency information */
+ if (!set_record(1))
+ return false;
+
+ return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+restore_mode(int old_enable, int old_record)
+{
+ /* Clear previous events and disable recording */
+ if (!set_record(old_record))
+ return false;
+
+ if (!set_enable(old_enable))
+ return false;
+
+ return true;
+}
+
+/* ************************************************************************ */
+
+static int
+gp_number(int channel)
+{
+ return channel - MALI_PROFILING_EVENT_CHANNEL_GP0;
+}
+
+/* ************************************************************************ */
+
+static int
+pp_number(int channel)
+{
+ return channel - MALI_PROFILING_EVENT_CHANNEL_PP0;
+}
+
+/* ************************************************************************ */
+
+static bool
+is_gp_channel(int channel)
+{
+ return (channel == MALI_PROFILING_EVENT_CHANNEL_GP0);
+}
+
+/* ************************************************************************ */
+
+static bool
+is_pp_channel(int channel)
+{
+ return (channel == MALI_PROFILING_EVENT_CHANNEL_PP0 ||
+ channel == MALI_PROFILING_EVENT_CHANNEL_PP1 ||
+ channel == MALI_PROFILING_EVENT_CHANNEL_PP2 ||
+ channel == MALI_PROFILING_EVENT_CHANNEL_PP3 ||
+ channel == MALI_PROFILING_EVENT_CHANNEL_PP4 ||
+ channel == MALI_PROFILING_EVENT_CHANNEL_PP5 ||
+ channel == MALI_PROFILING_EVENT_CHANNEL_PP6);
+}
+
+/* ************************************************************************ */
+
+static bool
+is_gp_or_pp_channel(int channel)
+{
+ return (is_gp_channel(channel) || is_pp_channel(channel));
+}
+
+/* ************************************************************************ */
+
+static bool
+is_known_channel(int channel)
+{
+ return (is_gp_or_pp_channel(channel) ||
+ channel == MALI_PROFILING_EVENT_CHANNEL_SOFTWARE ||
+ channel == MALI_PROFILING_EVENT_CHANNEL_GPU);
+}
+
+/* ************************************************************************ */
+
+static const char *
+get_gp_name(int channel)
+{
+ switch (channel) {
+ case MALI_PROFILING_EVENT_CHANNEL_GP0:
+ return "GP0";
+ default:
+ return "NOT A GP UNIT";
+ }
+}
+
+/* ************************************************************************ */
+
+static const char *
+get_pp_name(int channel)
+{
+ switch (channel) {
+ case MALI_PROFILING_EVENT_CHANNEL_PP0:
+ return "PP0";
+ case MALI_PROFILING_EVENT_CHANNEL_PP1:
+ return "PP1";
+ case MALI_PROFILING_EVENT_CHANNEL_PP2:
+ return "PP2";
+ case MALI_PROFILING_EVENT_CHANNEL_PP3:
+ return "PP3";
+ case MALI_PROFILING_EVENT_CHANNEL_PP4:
+ return "PP4";
+ case MALI_PROFILING_EVENT_CHANNEL_PP5:
+ return "PP5";
+ case MALI_PROFILING_EVENT_CHANNEL_PP6:
+ return "PP6";
+ case MALI_PROFILING_EVENT_CHANNEL_PP7:
+ return "PP7";
+ default:
+ return "NOT A PP UNIT";
+ }
+}
+
+/* ************************************************************************ */
+
+static const char *
+get_channel_name(int channel)
+{
+ if (is_pp_channel(channel))
+ return get_pp_name(channel);
+ else if (is_gp_channel(channel))
+ return get_gp_name(channel);
+ else if (channel == MALI_PROFILING_EVENT_CHANNEL_SOFTWARE)
+ return "Software";
+ else if (channel == MALI_PROFILING_EVENT_CHANNEL_GPU)
+ return "GPU";
+ else {
+ static char buf[64];
+
+ snprintf(buf, sizeof(buf), "UNKNOWN(%u)", channel);
+ return buf;
+ }
+}
+
+/* ************************************************************************ */
+
+static int
+print_date(debug_level_t level, const mali_event *ev)
+{
+ time_t t;
+ char str[256], stz[32];
+ struct tm *ltm;
+
+ if (level > debug_level)
+ return 0;
+
+ t = ev->timestamp / TIMESTAMP_FACTOR;
+ ltm = localtime(&t);
+
+ strftime(str, sizeof(str), "%F %T", ltm);
+ strftime(stz, sizeof(stz), "%z", ltm);
+
+ /* No newline at end */
+ return dprintf(level, "%s.%010u %s: ", str,
+ (unsigned int)(ev->timestamp % TIMESTAMP_FACTOR), stz);
+}
+
+/* ************************************************************************ */
+
+/* Channel is a GP channel here */
+static bool
+process_gp_job(mali_ctx *ctx, const mali_event *ev, int un)
+{
+ double freq, volt, duration, power;
+ process_info *proc;
+ pid_t pid;
+
+ if (ctx->gpu_state.freq == -1) {
+ dprintf(WARN, "GPU Frequency is unknown for finished job. "
+ "Set it to 440 MHz.\n");
+ ctx->gpu_state.freq = 440000000;
+ }
+
+ if (ctx->gpu_state.volt == -1) {
+ dprintf(WARN, "GPU Voltage is unknown for finished job. "
+ "Set it to 1.075 V.\n");
+ ctx->gpu_state.volt = 1075000;
+ }
+
+ freq = ctx->gpu_state.freq / 1000000.;
+ volt = ctx->gpu_state.volt / 1000000.;
+ pid = ctx->gpu_state.gp[un].pid;
+ ctx->gpu_state.gp[un].status = UNIT_FREE;
+ duration = (double)(ev->timestamp - ctx->gpu_state.gp[un].job_start) /
+ (double)TIMESTAMP_FACTOR;
+ /* Consumed power approximately can be computed as this:
+ * P = C * V^2 * f, where C is capacitance, V is voltage
+ * and f is frequency. We don't need exact Watts so we consider
+ * that 1 PW (PseudoWatt) is consumed running one GPU unit
+ * (GPx or PPx) on 266 MHz and 1 Volt. */
+ power = freq * volt * volt * duration / 266.;
+ dprintf(TASK_EVENTS, "Job was ran on GP%d by pid %d, tid %d "
+ "for %lg seconds with GPU frequency = %lg MHz, "
+ "voltage = %.03f, "
+ "consumed power = %lg PseudoWatts\n",
+ un, pid, ctx->gpu_state.gp[un].tid, duration, freq, volt,
+ power);
+
+ proc = get_proc_info(pid);
+ if (!proc)
+ add_unknown_proc(pid, power);
+ else
+ proc->power += power;
+
+ return true;
+}
+
+/* ************************************************************************ */
+
+/* Channel is a PP channel here */
+static bool
+process_pp_job(mali_ctx *ctx, const mali_event *ev, int un)
+{
+ double freq, volt, duration, power;
+ process_info *proc;
+ pid_t pid;
+
+ if (ctx->gpu_state.freq == -1) {
+ dprintf(WARN, "GPU Frequency is unknown for finished job. "
+ "Set it to 440 MHz.\n");
+ ctx->gpu_state.freq = 440000000;
+ }
+
+ if (ctx->gpu_state.volt == -1) {
+ dprintf(WARN, "GPU Voltage is unknown for finished job. "
+ "Set it to 1.075 V.\n");
+ ctx->gpu_state.volt = 1075000;
+ }
+
+ freq = ctx->gpu_state.freq / 1000000.;
+ volt = ctx->gpu_state.volt / 1000000.;
+ pid = ctx->gpu_state.pp[un].pid;
+ ctx->gpu_state.pp[un].status = UNIT_FREE;
+ duration = (double)(ev->timestamp - ctx->gpu_state.pp[un].job_start) /
+ (double)TIMESTAMP_FACTOR;
+ /* See comment in process_gp_job() above. */
+ power = freq * volt * volt * duration / 266.;
+ dprintf(TASK_EVENTS, "Job was ran on PP%d by pid %d, tid %d "
+ "for %lg seconds with GPU frequency = %g MHz, "
+ "voltage = %.03f, "
+ "consumed power = %lg PseudoWatts\n",
+ un, pid, ctx->gpu_state.pp[un].tid, duration, freq, volt,
+ power);
+
+ proc = get_proc_info(pid);
+ if (!proc)
+ add_unknown_proc(pid, power);
+ else
+ proc->power += power;
+
+ return true;
+}
+
+/* ************************************************************************ */
+
+/* Channel is a GP channel here */
+static bool
+process_gp_job_start(mali_ctx *ctx, const mali_event *ev)
+{
+ int un;
+
+ print_date(TASK_EVENTS, ev);
+ dprintf(TASK_EVENTS, "GP job started at %s by pid = %u, tid = %u\n",
+ get_gp_name(ev->channel), ev->d0, ev->d1);
+ un = gp_number(ev->channel);
+ if (ctx->gpu_state.gp[un].status != UNIT_FREE) {
+ edprintf(WARN, "We have missed GP Job Stop event. "
+ "Considering running task finished.\n");
+ if (!process_gp_job(ctx, ev, un))
+ return false;
+ }
+
+ ctx->gpu_state.gp[un].status = UNIT_BUSY;
+ ctx->gpu_state.gp[un].job_start = ev->timestamp;
+ ctx->gpu_state.gp[un].pid = ev->d0;
+ ctx->gpu_state.gp[un].tid = ev->d1;
+ return true;
+}
+
+/* ************************************************************************ */
+
+/* Channel is a PP channel here */
+static bool
+process_pp_job_start(mali_ctx *ctx, const mali_event *ev)
+{
+ int un;
+
+ print_date(TASK_EVENTS, ev);
+ dprintf(TASK_EVENTS, "PP job started at %s by pid = %u, tid = %u\n",
+ get_pp_name(ev->channel), ev->d0, ev->d1);
+ un = pp_number(ev->channel);
+ if (ctx->gpu_state.pp[un].status != UNIT_FREE) {
+ edprintf(WARN, "We have missed PP Job Stop event. "
+ "Considering running task finished.\n");
+ if (!process_pp_job(ctx, ev, un))
+ return false;
+ }
+
+ ctx->gpu_state.pp[un].status = UNIT_BUSY;
+ ctx->gpu_state.pp[un].job_start = ev->timestamp;
+ ctx->gpu_state.pp[un].pid = ev->d0;
+ ctx->gpu_state.pp[un].tid = ev->d1;
+ return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+process_start_event(mali_ctx *ctx, const mali_event *ev)
+{
+ /* Skip events that was before program start
+ * or after program finish */
+ if ((signed long long)ev->timestamp < ctx->start_time ||
+ (signed long long)ev->timestamp > ctx->finish_time) {
+ dprintf(MISC_EVENTS,
+ "Skipping Start event out of time interval (ts=%lld, "
+ "st=%lld, fn=%lld).\n",
+ ev->timestamp, ctx->start_time, ctx->finish_time);
+ return true;
+ }
+
+ if (!is_gp_or_pp_channel(ev->channel)) {
+ print_date(MISC_EVENTS, ev);
+ dprintf(MISC_EVENTS, "Skipping Start event from uninterested "
+ "channel %s.\n", get_channel_name(ev->channel));
+ return true;
+ }
+
+ if (is_gp_channel(ev->channel))
+ return process_gp_job_start(ctx, ev);
+ else
+ return process_pp_job_start(ctx, ev);
+}
+
+/* ************************************************************************ */
+
+/* Channel is a GP channel here */
+static bool
+process_gp_job_stop(mali_ctx *ctx, const mali_event *ev)
+{
+ int un;
+ int src0, src1;
+
+ src0 = ev->d2 & 0xff;
+ src1 = ev->d2 >> 8;
+ print_date(TASK_EVENTS, ev);
+ dprintf(TASK_EVENTS,
+ "GP job stopped at %s with val0 = %u, val1 = %u, "
+ "src0 = %u, src1 = %u\n",
+ get_gp_name(ev->channel), ev->d0, ev->d1, src0, src1);
+ un = gp_number(ev->channel);
+ if (ctx->gpu_state.gp[un].status != UNIT_BUSY) {
+ edprintf(WARN, "Skipping GP Job Stop event because unit "
+ "is already free.\n");
+ return true;
+ }
+
+ return process_gp_job(ctx, ev, un);
+}
+
+/* ************************************************************************ */
+
+/* Channel is a PP channel here */
+static bool
+process_pp_job_stop(mali_ctx *ctx, const mali_event *ev)
+{
+ int un;
+ int src0, src1;
+
+ src0 = ev->d2 & 0xff;
+ src1 = ev->d2 >> 8;
+ print_date(TASK_EVENTS, ev);
+ dprintf(TASK_EVENTS,
+ "PP job stopped at %s with val0 = %u, val1 = %u, "
+ "src0 = %u, src1 = %u\n",
+ get_pp_name(ev->channel), ev->d0, ev->d1, src0, src1);
+ un = pp_number(ev->channel);
+ if (ctx->gpu_state.pp[un].status != UNIT_BUSY) {
+ edprintf(WARN, "Skipping PP Job Stop event because unit "
+ "is already free.\n");
+ return true;
+ }
+
+ return process_pp_job(ctx, ev, un);
+}
+
+/* ************************************************************************ */
+
+static bool
+process_stop_event(mali_ctx *ctx, const mali_event *ev)
+{
+ /* Skip events that was before program start
+ * or after program finish */
+ if ((signed long long)ev->timestamp < ctx->start_time ||
+ (signed long long)ev->timestamp > ctx->finish_time) {
+ dprintf(MISC_EVENTS,
+ "Skipping Stop event out of time interval.\n");
+ return true;
+ }
+
+ if (!is_gp_or_pp_channel(ev->channel)) {
+ print_date(MISC_EVENTS, ev);
+ dprintf(MISC_EVENTS,
+ "Skipping Stop event from uninterested channel %s.\n",
+ get_channel_name(ev->channel));
+ return true;
+ }
+
+ if (is_gp_channel(ev->channel))
+ return process_gp_job_stop(ctx, ev);
+ else
+ return process_pp_job_stop(ctx, ev);
+}
+
+/* ************************************************************************ */
+
+static bool
+process_sw_global_try_lock(const mali_event *ev)
+{
+ print_date(MISC_EVENTS, ev);
+ dprintf(MISC_EVENTS,
+ "SW Global Try Lock by pid = %u, tid = %u, key = %u\n",
+ ev->d0, ev->d1, ev->d2);
+ return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+process_sw_global_lock(const mali_event *ev)
+{
+ print_date(MISC_EVENTS, ev);
+ dprintf(MISC_EVENTS,
+ "SW Global Lock by pid = %u, tid = %u, key = %u\n",
+ ev->d0, ev->d1, ev->d2);
+ return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+process_sw_global_unlock(const mali_event *ev)
+{
+ print_date(MISC_EVENTS, ev);
+ dprintf(MISC_EVENTS,
+ "SW Global Unlock by pid = %u, tid = %u, key = %u\n",
+ ev->d0, ev->d1, ev->d2);
+ return true;
+}
+
+/* ************************************************************************ */
+
+/* on:
+ * 0 -- Device is powering up,
+ * 1 -- Device is powering down,
+ * -1 -- Don't care about device state and don't issue warnings. */
+static void
+split_gp_jobs(mali_ctx *ctx, const mali_event *ev, int on)
+{
+ int i;
+
+ for (i = 0; i < GP_NUM; i++) {
+ if (ctx->gpu_state.gp[i].status == UNIT_FREE)
+ continue;
+
+ if (on == 1)
+ dprintf(WARN,
+ "Strange thing. GP%d is turned on while "
+ "task is already running.\n", i);
+ else if (on == 0)
+ dprintf(WARN,
+ "Strange thing. GP%d is turned off while "
+ "task is still running.\n", i);
+
+ dprintf(MISC_EVENTS, "Next GP job is splitted.\n");
+ /* Split task and "restart" job with the same pid */
+ process_gp_job(ctx, ev, i);
+ ctx->gpu_state.gp[i].status = UNIT_BUSY;
+ ctx->gpu_state.gp[i].job_start = ev->timestamp;
+ }
+}
+
+/* ************************************************************************ */
+
+static void
+split_pp_jobs(mali_ctx *ctx, const mali_event *ev, int on)
+{
+ int i;
+
+ for (i = 0; i < PP_NUM; i++) {
+ if (ctx->gpu_state.pp[i].status == UNIT_FREE)
+ continue;
+
+ if (on == 1)
+ dprintf(WARN,
+ "Strange thing. PP%d is turned on while "
+ "task is already running.\n", i);
+ else if (on == 0)
+ dprintf(WARN,
+ "Strange thing. PP%d is turned off while "
+ "task is still running.\n", i);
+
+ dprintf(MISC_EVENTS, "Next PP job is splitted.\n");
+ /* Split task and "restart" job with the same pid */
+ process_pp_job(ctx, ev, i);
+ ctx->gpu_state.pp[i].status = UNIT_BUSY;
+ ctx->gpu_state.pp[i].job_start = ev->timestamp;
+ }
+}
+
+/* ************************************************************************ */
+
+static void
+split_jobs(mali_ctx *ctx, const mali_event *ev, int on)
+{
+ /* It seems that it is impossible to change Only voltage without
+ * changing frequency. And it seems that these events are always
+ * come in pairs. So we split our jobs at first event and skip
+ * second to avoid reports with strange frequency / voltage
+ * combination. It would be better to know dependancy between
+ * frequency and voltage so we can adjust another when one changes.*/
+ if (ctx->gpu_state.split_count & 1)
+ return;
+
+ split_gp_jobs(ctx, ev, on);
+ split_pp_jobs(ctx, ev, on);
+}
+
+/* ************************************************************************ */
+
+static bool
+process_sw_gpu_freq(mali_ctx *ctx, const mali_event *ev)
+{
+ if (!ev->d1) {
+ print_date(MISC_EVENTS, ev);
+ dprintf(MISC_EVENTS, "SW GPU Freq rate (previous) = %u\n",
+ ev->d0);
+ return true;
+ }
+
+ if (ctx->gpu_state.freq == (signed int)ev->d0)
+ return true;
+
+ split_jobs(ctx, ev, -1);
+ ctx->gpu_state.freq = ev->d0;
+ ctx->gpu_state.split_count++;
+ print_date(TASK_EVENTS, ev);
+ dprintf(TASK_EVENTS, "GPU frequency changed to %d\n",
+ ctx->gpu_state.freq);
+ return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+process_sw_gpu_volts(mali_ctx *ctx, const mali_event *ev)
+{
+ if (ev->d2 != 2) {
+ print_date(MISC_EVENTS, ev);
+ dprintf(MISC_EVENTS,
+ "SW GPU Volts min_uV = %u, max_uV = %u (Desired)\n",
+ ev->d0, ev->d1);
+ return true;
+ }
+
+ if (ctx->gpu_state.volt == (signed int)ev->d0)
+ return true;
+
+ split_jobs(ctx, ev, -1);
+ ctx->gpu_state.volt = ev->d0;
+ ctx->gpu_state.split_count++;
+ print_date(TASK_EVENTS, ev);
+ dprintf(TASK_EVENTS, "GPU voltage changed to %d\n",
+ ctx->gpu_state.volt);
+ return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+process_single_software_event(mali_ctx *ctx, const mali_event *ev)
+{
+ switch (ev->data) {
+ case MALI_PROFILING_EVENT_REASON_SINGLE_SW_GPU_FREQ:
+ return process_sw_gpu_freq(ctx, ev);
+ case MALI_PROFILING_EVENT_REASON_SINGLE_SW_GPU_VOLTS:
+ return process_sw_gpu_volts(ctx, ev);
+ case MALI_PROFILING_EVENT_REASON_SINGLE_SW_GLOBAL_TRY_LOCK:
+ return process_sw_global_try_lock(ev);
+ case MALI_PROFILING_EVENT_REASON_SINGLE_SW_GLOBAL_LOCK:
+ return process_sw_global_lock(ev);
+ case MALI_PROFILING_EVENT_REASON_SINGLE_SW_GLOBAL_UNLOCK:
+ return process_sw_global_unlock(ev);
+ break;
+/* case MALI_PROFILING_EVENT_REASON_SINGLE_SW_VBLANK_VSYNC:*/
+/* case MALI_PROFILING_EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE:*/
+ default:
+ print_date(WARN, ev);
+ dprintf(WARN, "Skipping Single software event "
+ "with unknown data %d.\n", ev->data);
+ break;
+ }
+
+ return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+process_hw_interrupt(const mali_event *ev)
+{
+ if (!is_gp_or_pp_channel(ev->channel)) {
+ print_date(WARN, ev);
+ dprintf(WARN, "Skipping HW Interrupt event from wrong "
+ "channel %s.\n", get_channel_name(ev->channel));
+ return true;
+ }
+
+ print_date(MISC_EVENTS, ev);
+ dprintf(MISC_EVENTS, "HW Interrupt at %s with irq_readout = %u\n",
+ get_channel_name(ev->channel), ev->d0);
+ return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+process_hw_flush(const mali_event *ev)
+{
+ if (!is_gp_or_pp_channel(ev->channel)) {
+ print_date(WARN, ev);
+ dprintf(WARN, "Skipping HW Flush event from wrong "
+ "channel %s.\n", get_channel_name(ev->channel));
+ return true;
+ }
+
+ print_date(MISC_EVENTS, ev);
+ dprintf(MISC_EVENTS,
+ "HW Flush at %s with frame_builder_id = %u, flush_id, = %u\n",
+ get_channel_name(ev->channel), ev->d0, ev->d1);
+ return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+update_jobs(mali_ctx *ctx, const mali_event *ev, int new_freq, int new_volt)
+{
+ /* Nothing to change */
+ if (ctx->gpu_state.freq == new_freq &&
+ ctx->gpu_state.volt == new_volt)
+ return true;
+
+ if (new_freq) {
+ dprintf(TASK_EVENTS, "GPU is ON now\n");
+ /* Shouldn't have any jobs running but recheck it for sure */
+ split_jobs(ctx, ev, 1);
+ } else {
+ dprintf(TASK_EVENTS, "GPU is OFF now\n");
+ /* Shouldn't have any jobs running but recheck it for sure */
+ split_jobs(ctx, ev, 0);
+ }
+
+ ctx->gpu_state.freq = new_freq;
+ ctx->gpu_state.volt = new_volt;
+ ctx->gpu_state.split_count = 0;
+ dprintf(TASK_EVENTS, "GPU frequency changed to %d, voltage to %d\n",
+ ctx->gpu_state.freq, ctx->gpu_state.volt);
+ return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+process_gpu_freq_volt_change(mali_ctx *ctx, const mali_event *ev)
+{
+ if (ev->channel != MALI_PROFILING_EVENT_CHANNEL_GPU) {
+ print_date(WARN, ev);
+ dprintf(WARN,
+ "Skipping GPU Freq Volt event from wrong channel "
+ "%s.\n", get_channel_name(ev->channel));
+ return true;
+ }
+
+ dprintf(MISC_EVENTS, "GPU Volt Change with mali_gpu_clk = %u, "
+ "mali_gpu_vol / 1000 = %u\n",
+ ev->d0, ev->d1);
+ return update_jobs(ctx, ev, ev->d0 * 1000000, ev->d1 * 1000);
+}
+
+/* ************************************************************************ */
+
+static bool
+process_single_hardware_event(mali_ctx *ctx, const mali_event *ev)
+{
+ switch (ev->data) {
+ case MALI_PROFILING_EVENT_REASON_SINGLE_HW_INTERRUPT:
+ return process_hw_interrupt(ev);
+ case MALI_PROFILING_EVENT_REASON_SINGLE_HW_FLUSH:
+ return process_hw_flush(ev);
+ case MALI_PROFILING_EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE:
+ return process_gpu_freq_volt_change(ctx, ev);
+ default:
+ print_date(WARN, ev);
+ dprintf(WARN, "Skipping Single hardware event "
+ "with unknown data %d.\n", ev->data);
+ break;
+ }
+
+ return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+process_single_event(mali_ctx *ctx, const mali_event *ev)
+{
+ if (!is_known_channel(ev->channel)) {
+ print_date(WARN, ev);
+ dprintf(WARN,
+ "Skipping Single event with unknown channel %d.\n",
+ ev->channel);
+ return true;
+ }
+
+ if (ev->channel == MALI_PROFILING_EVENT_CHANNEL_SOFTWARE)
+ return process_single_software_event(ctx, ev);
+ else
+ return process_single_hardware_event(ctx, ev);
+}
+
+/* ************************************************************************ */
+
+static bool
+process_unknown_event(const mali_event *ev)
+{
+ print_date(WARN, ev);
+ dprintf(WARN,
+ "Skipping unknown event: type = %u, channel = %u, data = %u, "
+ "d0 = %u, d1 = %u, d2 = %u, d3 = %u, d4 = %u.\n",
+ ev->type, ev->channel, ev->data, ev->d0, ev->d1, ev->d2,
+ ev->d3, ev->d4);
+ return true;
+}
+
+
+/* ************************************************************************ */
+
+static bool
+process_mali_event(mali_ctx *ctx, const mali_event *ev)
+{
+ switch (ev->type) {
+ case MALI_PROFILING_EVENT_TYPE_SINGLE:
+ return process_single_event(ctx, ev);
+ case MALI_PROFILING_EVENT_TYPE_START:
+ return process_start_event(ctx, ev);
+ case MALI_PROFILING_EVENT_TYPE_STOP:
+ return process_stop_event(ctx, ev);
+ case MALI_PROFILING_EVENT_TYPE_SUSPEND:
+ print_date(MISC_EVENTS, ev);
+ dprintf(MISC_EVENTS, "Skipping Suspend event.\n");
+ break;
+ case MALI_PROFILING_EVENT_TYPE_RESUME:
+ print_date(MISC_EVENTS, ev);
+ dprintf(MISC_EVENTS, "Skipping Resume event.\n");
+ break;
+ default:
+ return process_unknown_event(ev);
+ }
+
+ return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+parse_mali_event(mali_ctx *ctx, const char *line)
+{
+ mali_event ev;
+ unsigned int event_id;
+
+ if (sscanf(line, "%llu %u %u %u %u %u %u", &ev.timestamp, &event_id,
+ &ev.d0, &ev.d1, &ev.d2, &ev.d3, &ev.d4) != 7) {
+ edprintf(CRIT, "Wrong event line: %s", line);
+ return false;
+ }
+
+ /* Doesn't skip events here because we need information about current
+ * voltage and frequency. Will skip only job-related events later. */
+
+ ev.type = (event_id >> 24) & 0xf;
+ ev.channel = (event_id >> 16) & 0xff;
+ ev.data = event_id & 0xffff;
+ return process_mali_event(ctx, &ev);
+}
+
+/* ************************************************************************ */
+
+bool
+process_mali_events(mali_ctx *ctx)
+{
+ FILE *f;
+ char line[4096];
+
+ if (!ctx || ctx->processed)
+ return false;
+
+ f = fopen(ctx->fname, "r");
+ if (!f) {
+ edprintf(CRIT, "Can't open \"%s\".\n", ctx->fname);
+ ctx->error_flag = 1;
+ return false;
+ }
+
+ while (fgets(line, sizeof(line), f))
+ if (parse_mali_event(ctx, line) == -1) {
+ ctx->error_flag = 1;
+ return false;
+ }
+
+ fclose(f);
+ ctx->processed = true;
+ return true;
+}
+
+/* ************************************************************************ */
+
+static void
+init_gpu_state(mali_ctx *ctx)
+{
+ memset(&ctx->gpu_state, '\0', sizeof(mali_state));
+ ctx->gpu_state.freq = -1; /* Unknown */
+ ctx->gpu_state.volt = -1; /* Unknown */
+}
+
+/* ************************************************************************ */
+
+bool
+cleanup_mali_capture(mali_ctx **ctx)
+{
+ if (!ctx || !*ctx)
+ return true;
+
+ clear_procs_info();
+
+ if ((*ctx)->live &&
+ !restore_mode((*ctx)->old_enable, (*ctx)->old_record))
+ return false;
+
+ free(*ctx);
+ *ctx = NULL;
+
+ return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+stat_callback(const process_info *pinfo, void *data UNUSED)
+{
+ const char *comm, *cmdline;
+
+ if (pinfo->power == 0) /* Skip empty */
+ return true;
+
+ dprintf(TOTAL_REPORT,
+ "Total consumed power by pid %d is %lg PseudoWatts*s\n",
+ pinfo->pid, pinfo->power);
+
+ if (!pinfo->unknown_name) {
+ comm = pinfo->comm;
+ cmdline = pinfo->cmdline;
+ } else if (!get_pt_proc_info(pinfo->pid, &comm, &cmdline)) {
+ comm = "(Unknown)";
+ cmdline = "(Unknown)";
+ }
+
+ report.begin_row(ROW_SOFTWARE);
+ report.begin_cell();
+ report.addf("%.4f PWs", pinfo->power);
+ report.begin_cell();
+ report.addf("%d", pinfo->pid);
+ report.begin_cell();
+ report.add(comm);
+ report.begin_cell();
+ report.add(cmdline);
+ return true;
+}
+
+/* ************************************************************************ */
+
+bool
+print_mali_stat(mali_ctx *ctx)
+{
+ char *oldlocale;
+
+ if (!ctx)
+ return false;
+
+ if (ctx->error_flag || ctx->started || !ctx->processed) {
+ edprintf(CRIT,
+ "Errors was encountered during processing '%s'\n",
+ ctx->fname);
+ return false;
+ }
+
+ oldlocale = setlocale(LC_NUMERIC, NULL);
+ oldlocale = strdup(oldlocale);
+ setlocale(LC_NUMERIC, "C"); /* For dots in %f in printf() */
+
+ report.add_header("Overview of MALI GPU power consumers");
+ report.begin_table(TABLE_WIDE);
+ report.begin_row();
+ report.begin_cell(CELL_SOFTWARE_PROCESS);
+ report.add("Power est. (PseudoWatts*s)");
+ report.begin_cell(CELL_SOFTWARE_DESCRIPTION);
+ report.add("PID");
+ report.begin_cell(CELL_SOFTWARE_DESCRIPTION);
+ report.add("Name");
+ report.begin_cell(CELL_SOFTWARE_DESCRIPTION);
+ report.add("Description");
+ print_sorted_procs(stat_callback, NULL);
+
+ setlocale(LC_NUMERIC, oldlocale);
+ free(oldlocale);
+ return true;
+}
+
+/* ************************************************************************ */
+
+bool
+stop_mali_capture(mali_ctx *ctx)
+{
+ if (!ctx || !ctx->started)
+ return false;
+
+ if (ctx->live) {
+ struct timeval tv;
+
+ if (!set_record(0)) /* Stop events recording */
+ return false;
+
+ gettimeofday(&tv, NULL);
+ ctx->finish_time = (tv.tv_sec * 1000000ll + tv.tv_usec) *
+ (TIMESTAMP_FACTOR / 1000000ll);
+ }
+
+ ctx->started = false;
+ return true;
+}
+
+/* ************************************************************************ */
+
+bool
+start_mali_capture(mali_ctx *ctx)
+{
+ if (!ctx || ctx->started)
+ return false;
+
+ clear_procs_info();
+ ctx->processed = false;
+ ctx->error_flag = false;
+ init_gpu_state(ctx);
+
+ if (ctx->live) {
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ /* Could be wrong if TIMESTAMP_FACTOR is not divisible
+ * by 1000000 or if TIMESTAMP_FACTOR is less than 1000000. */
+ ctx->start_time = (tv.tv_sec * 1000000ll + tv.tv_usec) *
+ (TIMESTAMP_FACTOR / 1000000ll);
+
+ if (!set_mode()) /* Start events recording */
+ return false;
+ } else {
+ ctx->start_time = -1;
+ ctx->finish_time = 0x7fffffffffffffffll;
+ }
+
+ ctx->started = true;
+ return true;
+}
+
+/* ************************************************************************ */
+
+mali_ctx *
+init_mali_capture_ex(bool live, const char *fname)
+{
+ mali_ctx *ctx;
+
+ ctx = (mali_ctx *)malloc(sizeof(mali_ctx));
+ if (!ctx) {
+ edprintf(CRIT, "No free memory.\n");
+ return NULL;
+ }
+
+ memset(ctx, '\0', sizeof(mali_ctx));
+ ctx->live = live;
+ ctx->fname = fname;
+ ctx->started = false;
+ ctx->processed = false;
+
+ if (live && !save_mode(&ctx->old_enable, &ctx->old_record)) {
+ free(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+/* ************************************************************************ */
+
+mali_ctx *
+init_mali_capture()
+{
+ return init_mali_capture_ex(true, MALI_PROFILING_EVENTS);
+}
+
+/* ************************************************************************ */
+
+#else /* !ARM */
+
+/* ************************************************************************ */
+
+mali_ctx *
+init_mali_capture()
+{
+ return NULL;
+}
+
+/* ************************************************************************ */
+
+bool
+start_mali_capture(mali_ctx *ctx UNUSED)
+{
+ return true;
+}
+
+/* ************************************************************************ */
+
+bool
+stop_mali_capture(mali_ctx *ctx UNUSED)
+{
+ return true;
+}
+
+/* ************************************************************************ */
+
+bool
+process_mali_events(mali_ctx *ctx UNUSED)
+{
+ return true;
+}
+
+/* ************************************************************************ */
+
+bool
+print_mali_stat(mali_ctx *ctx UNUSED)
+{
+ return true;
+}
+
+/* ************************************************************************ */
+
+bool
+cleanup_mali_capture(mali_ctx **ctx UNUSED)
+{
+ return true;
+}
+
+#endif /* !ARM */
--
1.7.5.4
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2012-12-25 20:58 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-12-25 20:58 [Powertop] [FYI][RFC][PATCH 2/2] MALI GPU profiling support Sergey Senozhatsky
-- strict thread matches above, loose matches on Subject: below --
2012-11-01 12:00 Igor Zhbanov
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.