| /* |
| * 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 "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: |
| 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) |
| 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)); |
| } |
| |
| 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; |
| |
| 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"); |
| } |