summaryrefslogtreecommitdiffstats
path: root/src/perf/perf_bundle.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:00:20 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:00:20 +0000
commitfcb4cb5c3d0fec0fede160d565134d553d783fb2 (patch)
tree7be42535554ca6badc1847d83ef123f4dc3c5506 /src/perf/perf_bundle.cpp
parentInitial commit. (diff)
downloadpowertop-fcb4cb5c3d0fec0fede160d565134d553d783fb2.tar.xz
powertop-fcb4cb5c3d0fec0fede160d565134d553d783fb2.zip
Adding upstream version 2.15.upstream/2.15upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/perf/perf_bundle.cpp')
-rw-r--r--src/perf/perf_bundle.cpp348
1 files changed, 348 insertions, 0 deletions
diff --git a/src/perf/perf_bundle.cpp b/src/perf/perf_bundle.cpp
new file mode 100644
index 0000000..3d216ff
--- /dev/null
+++ b/src/perf/perf_bundle.cpp
@@ -0,0 +1,348 @@
+/*
+ * Copyright 2010, Intel Corporation
+ *
+ * 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.
+ *
+ * Authors:
+ * Arjan van de Ven <arjan@linux.intel.com>
+ */
+#include <iostream>
+#include <malloc.h>
+#include <algorithm>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "perf_bundle.h"
+#include "perf_event.h"
+#include "perf.h"
+
+#include "../cpu/cpu.h"
+
+#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
+# define USE_DECLTYPE
+#endif
+
+class perf_bundle_event: public perf_event
+{
+public:
+ perf_bundle_event(void);
+ virtual void handle_event(struct perf_event_header *header, void *cookie);
+};
+
+perf_bundle_event::perf_bundle_event(void) : perf_event()
+{
+}
+
+
+void perf_bundle_event::handle_event(struct perf_event_header *header, void *cookie)
+{
+ unsigned char *buffer;
+ vector<void *> *vector;
+
+ buffer = (unsigned char *)malloc(header->size);
+ memcpy(buffer, header, header->size);
+
+#ifdef USE_DECLTYPE
+ vector = (decltype(vector))cookie;
+#else
+ vector = (typeof(vector))cookie;
+#endif
+ vector->push_back(buffer);
+}
+
+
+void perf_bundle::release(void)
+{
+ class perf_event *ev;
+ unsigned int i = 0;
+
+ for (i = 0; i < events.size(); i++) {
+ ev = events[i];
+ if (!ev)
+ continue;
+ ev->clear();
+ delete ev;
+ }
+ events.clear();
+
+ for (i = 0; i < event_names.size(); i++) {
+ free((void*)event_names[i]);
+ }
+ event_names.clear();
+
+ for(i = 0; i < records.size(); i++) {
+ free(records[i]);
+ }
+ records.clear();
+}
+
+static char * read_file(const char *file)
+{
+ char *buffer = NULL; /* quient gcc */
+ char buf[4096];
+ int len = 0;
+ int fd;
+ int r;
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0)
+ exit(-1);
+
+ while((r = read(fd, buf, 4096)) > 0) {
+ if (len) {
+ char *tmp = (char *)realloc(buffer, len + r + 1);
+ if (!tmp)
+ free(buffer);
+ buffer = tmp;
+ } else
+ buffer = (char *)malloc(r + 1);
+ if (!buffer)
+ goto out;
+ memcpy(buffer + len, buf, r);
+ len += r;
+ buffer[len] = '\0';
+ }
+out:
+ close(fd);
+ return buffer;
+}
+
+static void parse_event_format(const char *event_name)
+{
+ char *tptr;
+ char *name = strdup(event_name);
+ char *sys = strtok_r(name, ":", &tptr);
+ char *event = strtok_r(NULL, ":", &tptr);
+ char *file;
+ char *buf;
+
+ file = (char *)malloc(strlen(sys) + strlen(event) +
+ strlen("/sys/kernel/debug/tracing/events////format") + 2);
+ sprintf(file, "/sys/kernel/debug/tracing/events/%s/%s/format", sys, event);
+
+ buf = read_file(file);
+ free(file);
+ if (!buf) {
+ free(name);
+ return;
+ }
+
+ pevent_parse_event(perf_event::pevent, buf, strlen(buf), sys);
+ free(name);
+ free(buf);
+}
+
+bool perf_bundle::add_event(const char *event_name)
+{
+ unsigned int i;
+ int event_added = false;
+ class perf_event *ev;
+
+
+ for (i = 0; i < all_cpus.size(); i++) {
+
+ if (!all_cpus[i])
+ continue;
+
+ ev = new class perf_bundle_event();
+
+ ev->set_event_name(event_name);
+ ev->set_cpu(i);
+
+ if ((int)ev->trace_type >= 0) {
+ if (event_names.find(ev->trace_type) == event_names.end()) {
+ event_names[ev->trace_type] = strdup(event_name);
+ parse_event_format(event_name);
+ }
+ events.push_back(ev);
+ event_added = true;
+ } else {
+ delete ev;
+ }
+ }
+ return event_added;
+}
+
+void perf_bundle::start(void)
+{
+ unsigned int i;
+ class perf_event *ev;
+
+ for (i = 0; i < events.size(); i++) {
+ ev = events[i];
+ if (!ev)
+ continue;
+ ev->start();
+ }
+}
+void perf_bundle::stop(void)
+{
+ unsigned int i;
+ class perf_event *ev;
+
+ for (i = 0; i < events.size(); i++) {
+ ev = events[i];
+ if (!ev)
+ continue;
+ ev->stop();
+ }
+}
+void perf_bundle::clear(void)
+{
+ unsigned int i;
+
+ class perf_event *ev;
+
+ for (i = 0; i < events.size(); i++) {
+ ev = events[i];
+ if (!ev)
+ continue;
+ ev->clear();
+ }
+
+ for (i = 0; i < records.size(); i++) {
+ free(records[i]);
+ }
+ records.resize(0);
+}
+
+
+struct trace_entry {
+ uint64_t time;
+ uint32_t cpu;
+ uint32_t res;
+ __u32 size;
+} __attribute__((packed));;
+
+
+struct perf_sample {
+ struct perf_event_header header;
+ struct trace_entry trace;
+ unsigned char data[0];
+} __attribute__((packed));
+
+static uint64_t timestamp(perf_event_header *event)
+{
+ struct perf_sample *sample;
+
+ if (event->type != PERF_RECORD_SAMPLE)
+ return 0;
+
+ sample = (struct perf_sample *)event;
+
+#if 0
+ int i;
+ unsigned char *x;
+
+ printf("header:\n");
+ printf(" type is %x \n", sample->header.type);
+ printf(" misc is %x \n", sample->header.misc);
+ printf(" size is %i \n", sample->header.size);
+ printf("sample:\n");
+ printf(" time is %llx \n", sample->trace.time);
+ printf(" cpu is %i / %x \n", sample->trace.cpu, sample->trace.cpu);
+ printf(" res is %i / %x \n", sample->trace.res, sample->trace.res);
+ printf(" size is %i / %x \n", sample->trace.size, sample->trace.size);
+ printf(" type is %i / %x \n", sample->trace.type, sample->trace.type);
+ printf(" flags is %i / %x \n", sample->trace.flags, sample->trace.flags);
+ printf(" p/c is %i / %x \n", sample->trace.preempt_count, sample->trace.preempt_count);
+ printf(" pid is %i / %x \n", sample->trace.pid, sample->trace.pid);
+ printf(" lock dept is %i / %x \n", sample->trace.lock_depth, sample->trace.lock_depth);
+
+ x = (unsigned char *)sample;
+ for (i = 0; i < sample->header.size; i++)
+ printf("%02x ", *(x+i));
+ printf("\n");
+#endif
+ return sample->trace.time;
+
+}
+
+static bool event_sort_function (void *i, void *j)
+{
+ struct perf_event_header *I, *J;
+
+ I = (struct perf_event_header *) i;
+ J = (struct perf_event_header *) j;
+ return (timestamp(I)<timestamp(J));
+}
+
+/*
+ * sample's PERF_SAMPLE_CPU cpu nr is a raw_smp_processor_id() by the
+ * time of perf_event_output(), which may differ from struct perf_event
+ * cpu, thus we need to fix sample->trace.cpu.
+ */
+static void fixup_sample_trace_cpu(struct perf_sample *sample)
+{
+ struct event_format *event;
+ struct pevent_record rec;
+ unsigned long long cpu_nr;
+ int type;
+ int ret;
+
+ rec.data = &sample->data;
+ type = pevent_data_type(perf_event::pevent, &rec);
+ event = pevent_find_event(perf_event::pevent, type);
+ if (!event)
+ return;
+ /** don't touch trace if event does not contain cpu_id field*/
+ ret = pevent_get_field_val(NULL, event, "cpu_id", &rec, &cpu_nr, 0);
+ if (ret < 0)
+ return;
+ sample->trace.cpu = cpu_nr;
+}
+
+void perf_bundle::process(void)
+{
+ unsigned int i;
+ class perf_event *ev;
+
+ /* fixme: reserve enough space in the array in one go */
+ for (i = 0; i < events.size(); i++) {
+ ev = events[i];
+ if (!ev)
+ continue;
+ ev->process(&records);
+ }
+ sort(records.begin(), records.end(), event_sort_function);
+
+ for (i = 0; i < records.size(); i++) {
+ struct perf_sample *sample;
+
+ sample = (struct perf_sample *)records[i];
+ if (!sample)
+ continue;
+
+ if (sample->header.type != PERF_RECORD_SAMPLE)
+ continue;
+
+ fixup_sample_trace_cpu(sample);
+ handle_trace_point(&sample->data, sample->trace.cpu, sample->trace.time);
+ }
+}
+
+void perf_bundle::handle_trace_point(void *trace, int cpu, uint64_t time)
+{
+ printf("UH OH... abstract handle_trace_point called\n");
+}