Minor cleanups
[meego-developer-tools:powertop.git] / perf.c
1 /*
2  * Copyright 2009, Intel Corporation
3  *
4  * This file is part of PowerTOP
5  *
6  * Portions borrowed from the "perf" tool (C) Ingo Molnar and others
7  *
8  * This program file is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation; version 2 of the License.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program in a file named COPYING; if not, write to the
19  * Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301 USA
22  *
23  * Authors:
24  *      Arjan van de Ven <arjan@linux.intel.com>
25  */
26
27 #define _GNU_SOURCE        /* or _BSD_SOURCE or _SVID_SOURCE */
28 #include <unistd.h>
29 #include <sys/syscall.h>   /* For SYS_xxx definitions */
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <sys/mman.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <sys/ioctl.h>
38
39 #include <fcntl.h>
40
41 #include "perf_event.h"
42
43 #include "powertop.h"
44
45
46 /* some people have stale headers */
47 #ifndef __NR_perf_event_open
48 #ifdef __i386__
49 #define __NR_perf_event_open    336
50 #endif
51 #if __x86_64__
52 #define __NR_perf_event_open    298
53 #endif
54 #endif
55
56 static int perf_fd = -1;
57 static void * perf_mmap;
58 static void * data_mmap;
59
60 static struct perf_event_mmap_page *pc;
61
62 static inline int
63 sys_perf_event_open(struct perf_event_attr *attr,
64                       pid_t pid, int cpu, int group_fd,
65                       unsigned long flags)
66 {
67         attr->size = sizeof(*attr);
68         return syscall(__NR_perf_event_open, attr, pid, cpu,
69                         group_fd, flags);
70 }
71
72 static int this_trace = 0;
73
74 static int get_trace_type(void)
75 {
76         FILE *file;
77         char line[4096];
78         file = fopen("/sys/kernel/debug/tracing/events/vfs/dirty_inode/id", "r");
79         if (!file)
80                 return 0;
81
82         if (fgets(line, 4095, file) == NULL)
83                 return 0;
84
85         this_trace = strtoull(line, NULL, 10);
86         fclose(file);
87         return this_trace;
88 }
89
90 static void create_perf_event(void)
91 {
92         struct perf_event_attr attr;
93         int ret;
94
95         struct {
96                 __u64 count;
97                 __u64 time_enabled;
98                 __u64 time_running;
99                 __u64 id;
100         } read_data;
101
102         memset(&attr, 0, sizeof(attr));
103
104         attr.read_format        = PERF_FORMAT_TOTAL_TIME_ENABLED |
105                                   PERF_FORMAT_TOTAL_TIME_RUNNING |
106                                   PERF_FORMAT_ID;
107
108         attr.sample_freq        = 0;
109         attr.sample_period      = 1;
110         attr.sample_type        |= PERF_SAMPLE_RAW;
111
112         attr.mmap               = 1;
113         attr.comm               = 1;
114         attr.inherit            = 0;
115         attr.disabled           = 1;
116
117         attr.type               = PERF_TYPE_TRACEPOINT;
118         attr.config             = get_trace_type();
119
120         if (attr.config <= 0)
121                 return;
122
123         perf_fd = sys_perf_event_open(&attr, -1, 0, -1, 0);
124
125         if (perf_fd < 0) {
126                 fprintf(stderr, "Perf syscall failed: %i / %i (%s)\n", perf_fd, errno, strerror(errno));
127                 return;
128         }
129         if (read(perf_fd, &read_data, sizeof(read_data)) == -1) {
130                 perror("Unable to read perf file descriptor\n");
131                 exit(-1);
132         }
133
134         fcntl(perf_fd, F_SETFL, O_NONBLOCK);
135
136         perf_mmap = mmap(NULL, (128+1)*getpagesize(),
137                                 PROT_READ | PROT_WRITE, MAP_SHARED, perf_fd, 0);
138         if (perf_mmap == MAP_FAILED) {
139                 fprintf(stderr, "failed to mmap with %d (%s)\n", errno, strerror(errno));
140                 return;
141         }
142
143         ret = ioctl(perf_fd, PERF_EVENT_IOC_ENABLE);
144
145         if (ret < 0)
146                 fprintf(stderr, "failed to enable perf \n");
147
148         pc = perf_mmap;
149         data_mmap = perf_mmap + getpagesize();
150
151
152 }
153
154
155 void start_data_dirty_capture(void)
156 {
157         create_perf_event();
158 }
159
160 void end_data_dirty_capture(void)
161 {
162         if (perf_fd >= 0)
163                 close(perf_fd);
164         perf_fd = -1;
165 }
166
167 struct trace_entry {
168         __u32                   size;
169         unsigned short          type;
170         unsigned char           flags;
171         unsigned char           preempt_count;
172         int                     pid;
173         int                     tgid;
174 };
175
176 #define TASK_COMM_LEN 16
177 struct dirty_inode {
178         char comm[TASK_COMM_LEN];
179         int  pid;
180         char dev[16];
181         char file[32];
182 };
183
184
185 struct sample_event{
186         struct perf_event_header        header;
187         struct trace_entry              trace;
188         struct dirty_inode              inode;
189 };
190
191
192 static void parse_event(void *ptr, int verbose)
193 {
194         char line[8192];
195         char pid[14];
196         char realcomm[17];
197         int suggested = 0;
198         struct sample_event *event = ptr;
199
200         memset(line, 0, sizeof(line));
201         memset(realcomm, 0, 17);
202
203         if (event->trace.type != this_trace)
204                 return;
205
206         if (event->trace.size < sizeof(struct dirty_inode))
207                 return;
208
209         if (event->inode.pid == 0)
210                 return;
211
212         memcpy(realcomm, event->inode.comm, 16);
213
214         if (strcmp(realcomm, "powertop") == 0)
215                 return;
216         /*
217          * btrfs kernel threads are internal and only
218          * do IO on behalf of others that also got recorded
219          */
220
221         if (strcmp(realcomm, "btrfs-") == 0)
222                 return;
223         /*
224          * don't record "IO" to tmpfs or /proc
225          */
226         if (strcmp(event->inode.dev, "tmpfs") == 0)
227                 return;
228         if (strcmp(event->inode.dev, "proc") == 0)
229                 return;
230         if (strcmp(event->inode.dev, "pipefs") == 0)
231                 return;
232         if (strcmp(event->inode.dev, "sysfs") == 0)
233                 return;
234         if (strcmp(event->inode.dev, "anon_inodefs") == 0)
235                 return;
236
237         sprintf(pid, "%i", event->inode.pid);
238         sprintf(line, "%s", realcomm);
239         push_line_pid(line, 0, 1, pid);
240
241         if (!suggested && strcmp(event->inode.file, "?")) {
242                 suggested = 1;
243                 sprintf(line,_("The program '%s' is writing to file '%s' on /dev/%s.\nThis prevents the disk from going to powersave mode."),
244                         realcomm, event->inode.file, event->inode.dev);
245                 add_suggestion(line, 30, 0, NULL, NULL);
246         }
247         if (verbose)
248                 printf(_("The application '%s' is writing to file '%s' on /dev/%s\n"),
249                         realcomm, event->inode.file, event->inode.dev);
250
251 }
252
253 void parse_data_dirty_buffer(void)
254 {
255         struct perf_event_header *header;
256         int i = 0;
257
258         if (perf_fd < 0)
259                 return;
260
261         if (dump)
262                 printf(_("Disk accesses:\n"));
263
264         while (pc->data_tail != pc->data_head && i++ < 5000) {
265                 while (pc->data_tail >= 128U * getpagesize())
266                         pc->data_tail -= 128 * getpagesize();
267
268                 header = data_mmap + pc->data_tail;
269
270                 if (header->size == 0)
271                         break;
272
273                 pc->data_tail += header->size;
274
275                 while (pc->data_tail >= 128U * getpagesize())
276                         pc->data_tail -= 128 * getpagesize();
277
278                 if (header->type == PERF_RECORD_SAMPLE)
279                         parse_event(header, dump);
280         }
281         pc->data_tail = pc->data_head;
282 }