summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--LICENSE339
-rw-r--r--Makefile25
-rw-r--r--README_Streamline.txt117
-rw-r--r--gator.h38
-rw-r--r--gator_annotate.c135
-rw-r--r--gator_backtrace.c68
-rw-r--r--gator_cookies.c234
-rw-r--r--gator_events.c37
-rw-r--r--gator_events.h19
-rw-r--r--gator_events_armv6.c250
-rw-r--r--gator_events_armv7.c427
-rw-r--r--gator_events_block.c180
-rw-r--r--gator_events_irq.c178
-rw-r--r--gator_events_meminfo.c203
-rw-r--r--gator_events_sched.c123
-rw-r--r--gator_fs.c270
-rw-r--r--gator_main.c990
-rw-r--r--gator_trace.h26
-rw-r--r--gator_trace_sched.c269
19 files changed, 3928 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,339 @@
1 GNU GENERAL PUBLIC LICENSE
2 Version 2, June 1991
3
4 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 Everyone is permitted to copy and distribute verbatim copies
7 of this license document, but changing it is not allowed.
8
9 Preamble
10
11 The licenses for most software are designed to take away your
12freedom to share and change it. By contrast, the GNU General Public
13License is intended to guarantee your freedom to share and change free
14software--to make sure the software is free for all its users. This
15General Public License applies to most of the Free Software
16Foundation's software and to any other program whose authors commit to
17using it. (Some other Free Software Foundation software is covered by
18the GNU Lesser General Public License instead.) You can apply it to
19your programs, too.
20
21 When we speak of free software, we are referring to freedom, not
22price. Our General Public Licenses are designed to make sure that you
23have the freedom to distribute copies of free software (and charge for
24this service if you wish), that you receive source code or can get it
25if you want it, that you can change the software or use pieces of it
26in new free programs; and that you know you can do these things.
27
28 To protect your rights, we need to make restrictions that forbid
29anyone to deny you these rights or to ask you to surrender the rights.
30These restrictions translate to certain responsibilities for you if you
31distribute copies of the software, or if you modify it.
32
33 For example, if you distribute copies of such a program, whether
34gratis or for a fee, you must give the recipients all the rights that
35you have. You must make sure that they, too, receive or can get the
36source code. And you must show them these terms so they know their
37rights.
38
39 We protect your rights with two steps: (1) copyright the software, and
40(2) offer you this license which gives you legal permission to copy,
41distribute and/or modify the software.
42
43 Also, for each author's protection and ours, we want to make certain
44that everyone understands that there is no warranty for this free
45software. If the software is modified by someone else and passed on, we
46want its recipients to know that what they have is not the original, so
47that any problems introduced by others will not reflect on the original
48authors' reputations.
49
50 Finally, any free program is threatened constantly by software
51patents. We wish to avoid the danger that redistributors of a free
52program will individually obtain patent licenses, in effect making the
53program proprietary. To prevent this, we have made it clear that any
54patent must be licensed for everyone's free use or not licensed at all.
55
56 The precise terms and conditions for copying, distribution and
57modification follow.
58
59 GNU GENERAL PUBLIC LICENSE
60 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
62 0. This License applies to any program or other work which contains
63a notice placed by the copyright holder saying it may be distributed
64under the terms of this General Public License. The "Program", below,
65refers to any such program or work, and a "work based on the Program"
66means either the Program or any derivative work under copyright law:
67that is to say, a work containing the Program or a portion of it,
68either verbatim or with modifications and/or translated into another
69language. (Hereinafter, translation is included without limitation in
70the term "modification".) Each licensee is addressed as "you".
71
72Activities other than copying, distribution and modification are not
73covered by this License; they are outside its scope. The act of
74running the Program is not restricted, and the output from the Program
75is covered only if its contents constitute a work based on the
76Program (independent of having been made by running the Program).
77Whether that is true depends on what the Program does.
78
79 1. You may copy and distribute verbatim copies of the Program's
80source code as you receive it, in any medium, provided that you
81conspicuously and appropriately publish on each copy an appropriate
82copyright notice and disclaimer of warranty; keep intact all the
83notices that refer to this License and to the absence of any warranty;
84and give any other recipients of the Program a copy of this License
85along with the Program.
86
87You may charge a fee for the physical act of transferring a copy, and
88you may at your option offer warranty protection in exchange for a fee.
89
90 2. You may modify your copy or copies of the Program or any portion
91of it, thus forming a work based on the Program, and copy and
92distribute such modifications or work under the terms of Section 1
93above, provided that you also meet all of these conditions:
94
95 a) You must cause the modified files to carry prominent notices
96 stating that you changed the files and the date of any change.
97
98 b) You must cause any work that you distribute or publish, that in
99 whole or in part contains or is derived from the Program or any
100 part thereof, to be licensed as a whole at no charge to all third
101 parties under the terms of this License.
102
103 c) If the modified program normally reads commands interactively
104 when run, you must cause it, when started running for such
105 interactive use in the most ordinary way, to print or display an
106 announcement including an appropriate copyright notice and a
107 notice that there is no warranty (or else, saying that you provide
108 a warranty) and that users may redistribute the program under
109 these conditions, and telling the user how to view a copy of this
110 License. (Exception: if the Program itself is interactive but
111 does not normally print such an announcement, your work based on
112 the Program is not required to print an announcement.)
113
114These requirements apply to the modified work as a whole. If
115identifiable sections of that work are not derived from the Program,
116and can be reasonably considered independent and separate works in
117themselves, then this License, and its terms, do not apply to those
118sections when you distribute them as separate works. But when you
119distribute the same sections as part of a whole which is a work based
120on the Program, the distribution of the whole must be on the terms of
121this License, whose permissions for other licensees extend to the
122entire whole, and thus to each and every part regardless of who wrote it.
123
124Thus, it is not the intent of this section to claim rights or contest
125your rights to work written entirely by you; rather, the intent is to
126exercise the right to control the distribution of derivative or
127collective works based on the Program.
128
129In addition, mere aggregation of another work not based on the Program
130with the Program (or with a work based on the Program) on a volume of
131a storage or distribution medium does not bring the other work under
132the scope of this License.
133
134 3. You may copy and distribute the Program (or a work based on it,
135under Section 2) in object code or executable form under the terms of
136Sections 1 and 2 above provided that you also do one of the following:
137
138 a) Accompany it with the complete corresponding machine-readable
139 source code, which must be distributed under the terms of Sections
140 1 and 2 above on a medium customarily used for software interchange; or,
141
142 b) Accompany it with a written offer, valid for at least three
143 years, to give any third party, for a charge no more than your
144 cost of physically performing source distribution, a complete
145 machine-readable copy of the corresponding source code, to be
146 distributed under the terms of Sections 1 and 2 above on a medium
147 customarily used for software interchange; or,
148
149 c) Accompany it with the information you received as to the offer
150 to distribute corresponding source code. (This alternative is
151 allowed only for noncommercial distribution and only if you
152 received the program in object code or executable form with such
153 an offer, in accord with Subsection b above.)
154
155The source code for a work means the preferred form of the work for
156making modifications to it. For an executable work, complete source
157code means all the source code for all modules it contains, plus any
158associated interface definition files, plus the scripts used to
159control compilation and installation of the executable. However, as a
160special exception, the source code distributed need not include
161anything that is normally distributed (in either source or binary
162form) with the major components (compiler, kernel, and so on) of the
163operating system on which the executable runs, unless that component
164itself accompanies the executable.
165
166If distribution of executable or object code is made by offering
167access to copy from a designated place, then offering equivalent
168access to copy the source code from the same place counts as
169distribution of the source code, even though third parties are not
170compelled to copy the source along with the object code.
171
172 4. You may not copy, modify, sublicense, or distribute the Program
173except as expressly provided under this License. Any attempt
174otherwise to copy, modify, sublicense or distribute the Program is
175void, and will automatically terminate your rights under this License.
176However, parties who have received copies, or rights, from you under
177this License will not have their licenses terminated so long as such
178parties remain in full compliance.
179
180 5. You are not required to accept this License, since you have not
181signed it. However, nothing else grants you permission to modify or
182distribute the Program or its derivative works. These actions are
183prohibited by law if you do not accept this License. Therefore, by
184modifying or distributing the Program (or any work based on the
185Program), you indicate your acceptance of this License to do so, and
186all its terms and conditions for copying, distributing or modifying
187the Program or works based on it.
188
189 6. Each time you redistribute the Program (or any work based on the
190Program), the recipient automatically receives a license from the
191original licensor to copy, distribute or modify the Program subject to
192these terms and conditions. You may not impose any further
193restrictions on the recipients' exercise of the rights granted herein.
194You are not responsible for enforcing compliance by third parties to
195this License.
196
197 7. If, as a consequence of a court judgment or allegation of patent
198infringement or for any other reason (not limited to patent issues),
199conditions are imposed on you (whether by court order, agreement or
200otherwise) that contradict the conditions of this License, they do not
201excuse you from the conditions of this License. If you cannot
202distribute so as to satisfy simultaneously your obligations under this
203License and any other pertinent obligations, then as a consequence you
204may not distribute the Program at all. For example, if a patent
205license would not permit royalty-free redistribution of the Program by
206all those who receive copies directly or indirectly through you, then
207the only way you could satisfy both it and this License would be to
208refrain entirely from distribution of the Program.
209
210If any portion of this section is held invalid or unenforceable under
211any particular circumstance, the balance of the section is intended to
212apply and the section as a whole is intended to apply in other
213circumstances.
214
215It is not the purpose of this section to induce you to infringe any
216patents or other property right claims or to contest validity of any
217such claims; this section has the sole purpose of protecting the
218integrity of the free software distribution system, which is
219implemented by public license practices. Many people have made
220generous contributions to the wide range of software distributed
221through that system in reliance on consistent application of that
222system; it is up to the author/donor to decide if he or she is willing
223to distribute software through any other system and a licensee cannot
224impose that choice.
225
226This section is intended to make thoroughly clear what is believed to
227be a consequence of the rest of this License.
228
229 8. If the distribution and/or use of the Program is restricted in
230certain countries either by patents or by copyrighted interfaces, the
231original copyright holder who places the Program under this License
232may add an explicit geographical distribution limitation excluding
233those countries, so that distribution is permitted only in or among
234countries not thus excluded. In such case, this License incorporates
235the limitation as if written in the body of this License.
236
237 9. The Free Software Foundation may publish revised and/or new versions
238of the General Public License from time to time. Such new versions will
239be similar in spirit to the present version, but may differ in detail to
240address new problems or concerns.
241
242Each version is given a distinguishing version number. If the Program
243specifies a version number of this License which applies to it and "any
244later version", you have the option of following the terms and conditions
245either of that version or of any later version published by the Free
246Software Foundation. If the Program does not specify a version number of
247this License, you may choose any version ever published by the Free Software
248Foundation.
249
250 10. If you wish to incorporate parts of the Program into other free
251programs whose distribution conditions are different, write to the author
252to ask for permission. For software which is copyrighted by the Free
253Software Foundation, write to the Free Software Foundation; we sometimes
254make exceptions for this. Our decision will be guided by the two goals
255of preserving the free status of all derivatives of our free software and
256of promoting the sharing and reuse of software generally.
257
258 NO WARRANTY
259
260 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268REPAIR OR CORRECTION.
269
270 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278POSSIBILITY OF SUCH DAMAGES.
279
280 END OF TERMS AND CONDITIONS
281
282 How to Apply These Terms to Your New Programs
283
284 If you develop a new program, and you want it to be of the greatest
285possible use to the public, the best way to achieve this is to make it
286free software which everyone can redistribute and change under these terms.
287
288 To do so, attach the following notices to the program. It is safest
289to attach them to the start of each source file to most effectively
290convey the exclusion of warranty; and each file should have at least
291the "copyright" line and a pointer to where the full notice is found.
292
293 <one line to give the program's name and a brief idea of what it does.>
294 Copyright (C) <year> <name of author>
295
296 This program is free software; you can redistribute it and/or modify
297 it under the terms of the GNU General Public License as published by
298 the Free Software Foundation; either version 2 of the License, or
299 (at your option) any later version.
300
301 This program is distributed in the hope that it will be useful,
302 but WITHOUT ANY WARRANTY; without even the implied warranty of
303 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 GNU General Public License for more details.
305
306 You should have received a copy of the GNU General Public License along
307 with this program; if not, write to the Free Software Foundation, Inc.,
308 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309
310Also add information on how to contact you by electronic and paper mail.
311
312If the program is interactive, make it output a short notice like this
313when it starts in an interactive mode:
314
315 Gnomovision version 69, Copyright (C) year name of author
316 Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 This is free software, and you are welcome to redistribute it
318 under certain conditions; type `show c' for details.
319
320The hypothetical commands `show w' and `show c' should show the appropriate
321parts of the General Public License. Of course, the commands you use may
322be called something other than `show w' and `show c'; they could even be
323mouse-clicks or menu items--whatever suits your program.
324
325You should also get your employer (if you work as a programmer) or your
326school, if any, to sign a "copyright disclaimer" for the program, if
327necessary. Here is a sample; alter the names:
328
329 Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
332 <signature of Ty Coon>, 1 April 1989
333 Ty Coon, President of Vice
334
335This General Public License does not permit incorporating your program into
336proprietary programs. If your program is a subroutine library, you may
337consider it more useful to permit linking proprietary applications with the
338library. If this is what you want to do, use the GNU Lesser General
339Public License instead of this License.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8e679a7
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,25 @@
1ifneq ($(KERNELRELEASE),)
2
3obj-m := gator.o
4
5gator-objs := gator_main.o \
6 gator_events_armv6.o \
7 gator_events_armv7.o \
8 gator_events_irq.o \
9 gator_events_sched.o \
10 gator_events_block.o \
11 gator_events_meminfo.o
12
13else
14
15all:
16 @echo
17 @echo "usage:"
18 @echo " make -C <kernel_build_dir> M=\`pwd\` ARCH=arm CROSS_COMPILE=<...> modules"
19 @echo
20 $(error)
21
22clean:
23 rm -f *.o .*.cmd */*.o */.*.cmd modules.order Module.symvers gator.ko gator.mod.c
24
25endif
diff --git a/README_Streamline.txt b/README_Streamline.txt
new file mode 100644
index 0000000..4c52b7d
--- /dev/null
+++ b/README_Streamline.txt
@@ -0,0 +1,117 @@
1
2*** Purpose ***
3
4Instructions on setting up ARM Streamline on the target.
5The gator driver and gator daemon are required to run on the ARM linux target in order for ARM Streamline to operate.
6The driver should be built as a module and the daemon must run with root permissions on the target.
7
8*** Preparing and building the kernel ***
9
10cd into the root source dir of the linux kernel
11make ARCH=arm CROSS_COMPILE=${CROSS_TOOLS}/bin/arm-none-linux-gnueabi- <platform_defconfig> (choose the appropriate configuration for your board)
12make ARCH=arm CROSS_COMPILE=${CROSS_TOOLS}/bin/arm-none-linux-gnueabi- menuconfig
13
14Required Kernel Changes (depending on the kernel version, the location of these configuration settings within menuconfig may be different)
15- General Setup
16 - [*] Profiling Support
17- Kernel hacking
18 - [*] Tracers
19 - [*] Trace process context switches and events
20- Kernel Features
21 - [*] High Resolution Timer Support
22
23The "context switches and events" option will not be available if other trace configurations are enabled. Other trace configurations being enabled is sufficient to turn on context switches and events.
24
25Optional Kernel Changes (depending on the kernel version, the location of these configuration settings within menuconfig may be different)
26Note: Configurations may not be supported on all targets
27- System Type
28 - [*] <SoC name> debugging peripherals (enable core performance counters on supported SoCs) /* kernels before 2.6.35 */
29
30make -j5 ARCH=arm CROSS_COMPILE=${CROSS_TOOLS}/bin/arm-none-linux-gnueabi- uImage
31
32*** Building the gator module ***
33
34To create the gator.ko module,
35 cd /ds-5-install-directory/arm/src
36 tar xzf gator-driver.tar.gz
37 cd gator-driver
38 make -C <kernel_build_dir> M=`pwd` ARCH=arm CROSS_COMPILE=<...> modules
39for example
40 make -C /home/username/kernel_2.6.32/ M=`pwd` ARCH=arm CROSS_COMPILE=/home/username/CodeSourcery/Sourcery_G++_Lite/bin/arm-none-linux-gnueabi- modules
41If successful, a gator.ko module should be generated
42
43*** Compiling an application or shared library ***
44
45Recommended compiler settings:
46 "-g": Debug symbols needed for best analysis results.
47 "-fno-inline": Speed improvement when processing the image files and most accurate analysis results.
48 "-fno-omit-frame-pointer": ARM EABI frame pointers (Code Sourcery cross compiler) allow the call stack to be recorded with each sample taken when in ARM state (i.e. not -mthumb).
49
50*** Running gator ***
51
52Load the kernel onto the target and copy gatord and gator.ko into the target's filesystem.
53gatord is located in <installdir>/arm/armv5t/.
54Ensure gatord has execute permissions
55 chmod +x gatord
56gator.ko must be located in the same directory as gatord on the target.
57With root privileges, run the daemon
58 sudo ./gatord &
59
60*** Profiling the kernel (optional) ***
61
62make ARCH=arm CROSS_COMPILE=$(CROSS_TOOLS}/bin/arm-none-linux-gnueabi- menuconfig
63- Kernel Hacking
64 - [*] Compile the kernel with debug info
65
66make -j5 ARCH=arm CROSS_COMPILE=${CROSS_TOOLS}/bin/arm-none-linux-gnueabi- uImage
67Use vmlinux as the image for debug symbols in Streamline.
68Drivers may be profiled using this method by statically linking the driver into the kernel image.
69Note that the gator driver does not perform kernel call stack recording.
70
71*** Automatically start gator on boot (optional) ***
72
73cd /etc/init.d
74vi rungator.sh
75 #!/bin/bash
76 /path/to/gatord &
77update-rc.d rungator.sh defaults
78
79*** Driver Sources ***
80
81Gator Sources
82 backtrace.c
83 cpu_buffer.c
84 cpu_fifo.c
85 cpu_fifo.h
86 gator_annotate.c
87 gator_events_irq.c
88 gator_init.c
89 gator_interrupt.c
90 gator_pmnc_armv7.c
91 gator_pmnc_none.c
92 gator_schedtrace.c
93 gator_setup.c
94 gator_timer.c
95 gator_traceprobe.h
96
97Oprofile Sources // function names updated to avoid Oprofile collisions
98 buffer_sync.c
99 buffer_sync.h
100 cpu_buffer.h
101 event_buffer.c
102 event_buffer.h
103 oprof.h
104 oprofile.h
105 oprofile_files.c
106 oprofile_stats.c
107 oprofile_stats.h
108 timer_int.c
109
110Modified Oprofile Sources
111 buffer_sync.c // modify sample contract to allow bad 'mm' or cookie
112 oprofilefs.c // gatorfs and magic number
113 oprof.c // updated module name and author
114
115*** GPL License ***
116
117For license information, please see the file LICENSE.
diff --git a/gator.h b/gator.h
new file mode 100644
index 0000000..87ca107
--- /dev/null
+++ b/gator.h
@@ -0,0 +1,38 @@
1/**
2 * Copyright 2010 ARM, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9/******************************************************************************
10 * Filesystem
11 ******************************************************************************/
12int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root,
13 char const *name, const struct file_operations *fops, int perm);
14
15struct dentry *gatorfs_mkdir(struct super_block *sb,
16 struct dentry *root, char const *name);
17
18int gatorfs_create_ulong(struct super_block *sb, struct dentry *root,
19 char const *name, unsigned long *val);
20
21int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root,
22 char const *name, unsigned long *val);
23
24/******************************************************************************
25 * Events
26 ******************************************************************************/
27struct __gator_interface {
28 int (*create_files)(struct super_block *sb, struct dentry *root);
29 int (*init)(int *key);
30 int (*start)(void);
31 void (*stop)(void);
32 int (*read)(int **buffer);
33 struct __gator_interface *next;
34};
35
36typedef struct __gator_interface gator_interface;
37
38int gator_event_install(int (*event_install)(gator_interface *));
diff --git a/gator_annotate.c b/gator_annotate.c
new file mode 100644
index 0000000..1844157
--- /dev/null
+++ b/gator_annotate.c
@@ -0,0 +1,135 @@
1/**
2 * Copyright (C) ARM Limited 2010. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10#include <linux/slab.h>
11#include <linux/fs.h>
12#include <linux/mm.h>
13#include <linux/sched.h>
14#include <asm/uaccess.h>
15#include <asm/current.h>
16#include <linux/spinlock.h>
17
18#define INSTSIZE 1024
19static DEFINE_SPINLOCK(annotate_lock);
20static char *annotateBuf;
21static char *annotateBuf0;
22static char *annotateBuf1;
23static int annotatePos;
24static int annotateSel;
25
26static int gatorfs_copy_from_user(char* localbuf, char const __user *buf, size_t count)
27{
28 if (count == 0)
29 return 0;
30
31 if (copy_from_user(localbuf, buf, count))
32 return -EFAULT;
33
34 return 0;
35}
36
37static ssize_t annotate_write(struct file *file, char const __user *buf, size_t count, loff_t *offset)
38{
39 char tempBuffer[32];
40 unsigned long flags;
41 int retval, remaining, size;
42
43 if (*offset)
44 return -EINVAL;
45
46 // determine size to capture
47 remaining = INSTSIZE - annotatePos - 24; // leave some extra space
48 size = count < sizeof(tempBuffer) ? count : sizeof(tempBuffer);
49 size = size < remaining ? size : remaining;
50 if (size <= 0)
51 return 0;
52
53 // copy from user space
54 retval = gatorfs_copy_from_user(tempBuffer, buf, size);
55 if (retval == 0) {
56 // synchronize shared variables annotateBuf and annotatePos
57 spin_lock_irqsave(&annotate_lock, flags);
58 if (!annotateBuf) {
59 size = -EINVAL;
60 } else {
61 *(int*)&annotateBuf[annotatePos + 0] = current->pid; // thread id
62 *(int*)&annotateBuf[annotatePos + 4] = size; // length in bytes
63 memcpy(&annotateBuf[annotatePos + 8], tempBuffer, size); // data
64 annotatePos = annotatePos + 8 + size; // increment position
65 annotatePos = (annotatePos + 3) & ~3; // align to 4-byte boundary
66 }
67 spin_unlock_irqrestore(&annotate_lock, flags);
68
69 // return the number of bytes written
70 retval = size;
71 }
72
73 return retval;
74}
75
76static const struct file_operations annotate_fops = {
77 .write = annotate_write
78};
79
80int gator_annotate_create_files(struct super_block *sb, struct dentry *root)
81{
82 annotateBuf = annotateBuf0 = annotateBuf1 = NULL;
83 return gatorfs_create_file_perm(sb, root, "annotate", &annotate_fops, 0666);
84}
85
86int gator_annotate_init(void)
87{
88 return 0;
89}
90
91int gator_annotate_start(void)
92{
93 annotatePos = annotateSel = 0;
94 annotateBuf0 = kmalloc(INSTSIZE, GFP_KERNEL);
95 annotateBuf1 = kmalloc(INSTSIZE, GFP_KERNEL);
96 annotateBuf = annotateBuf0;
97 if (!annotateBuf0 || !annotateBuf1)
98 return -1;
99 return 0;
100}
101
102void gator_annotate_stop(void)
103{
104 spin_lock(&annotate_lock);
105
106 kfree(annotateBuf0);
107 kfree(annotateBuf1);
108 annotateBuf = annotateBuf0 = annotateBuf1 = NULL;
109
110 spin_unlock(&annotate_lock);
111}
112
113int gator_annotate_read(int **buffer)
114{
115 int len;
116
117 if (smp_processor_id() || !annotatePos || !annotateBuf)
118 return 0;
119
120 annotateSel = !annotateSel;
121
122 if (buffer)
123 *buffer = (int *)annotateBuf;
124
125 spin_lock(&annotate_lock);
126
127 len = annotatePos;
128 annotatePos = 0;
129 annotateBuf = annotateSel ? annotateBuf1 : annotateBuf0;
130
131 spin_unlock(&annotate_lock);
132
133 // Return number of 4-byte words
134 return len / 4;
135}
diff --git a/gator_backtrace.c b/gator_backtrace.c
new file mode 100644
index 0000000..c4ddf41
--- /dev/null
+++ b/gator_backtrace.c
@@ -0,0 +1,68 @@
1/**
2 * Copyright (C) ARM Limited 2010. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10/*
11 * EABI backtrace stores {fp,lr} on the stack.
12 */
13struct frame_tail_eabi {
14 unsigned long fp; // points to prev_lr
15 unsigned long lr;
16};
17
18static void arm_backtrace_eabi(int cpu, struct pt_regs * const regs, unsigned int depth)
19{
20#if defined(__arm__)
21 struct frame_tail_eabi *tail;
22 struct frame_tail_eabi *next;
23 struct frame_tail_eabi *ptrtail;
24 struct frame_tail_eabi buftail;
25 unsigned long fp = regs->ARM_fp;
26 unsigned long lr = regs->ARM_lr;
27 int is_user_mode = user_mode(regs);
28
29 if (!is_user_mode) {
30 return;
31 }
32
33 /* entry preamble may not have executed */
34 gator_add_trace(cpu, lr);
35
36 /* check tail is valid */
37 if (fp == 0) {
38 return;
39 }
40
41 tail = (struct frame_tail_eabi *)(fp - 4);
42
43 while (depth-- && tail && !((unsigned long) tail & 3)) {
44 /* Also check accessibility of one struct frame_tail beyond */
45 if (!access_ok(VERIFY_READ, tail, sizeof(struct frame_tail_eabi)))
46 return;
47 if (__copy_from_user_inatomic(&buftail, tail, sizeof(struct frame_tail_eabi)))
48 return;
49 ptrtail = &buftail;
50
51 lr = ptrtail[0].lr;
52 gator_add_trace(cpu, lr);
53
54 /* frame pointers should progress back up the stack, towards higher addresses */
55 next = (struct frame_tail_eabi *)(lr - 4);
56 if (tail >= next || lr == 0) {
57 fp = ptrtail[0].fp;
58 next = (struct frame_tail_eabi *)(fp - 4);
59 /* check tail is valid */
60 if (tail >= next || fp == 0) {
61 return;
62 }
63 }
64
65 tail = next;
66 }
67#endif
68}
diff --git a/gator_cookies.c b/gator_cookies.c
new file mode 100644
index 0000000..d267a57
--- /dev/null
+++ b/gator_cookies.c
@@ -0,0 +1,234 @@
1/**
2 * Copyright (C) ARM Limited 2010. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10#define COOKIEMAP_ENTRIES 1024 /* must be power of 2 */
11#define MAX_COLLISIONS 2
12
13static DEFINE_PER_CPU(uint32_t, cookie_next_key);
14static DEFINE_PER_CPU(uint32_t, cookie_prev_text);
15static DEFINE_PER_CPU(uint32_t, cookie_prev_value);
16static DEFINE_PER_CPU(uint64_t *, cookie_keys);
17static DEFINE_PER_CPU(uint32_t *, cookie_values);
18
19static uint32_t *gator_crc32_table;
20
21static uint32_t cookiemap_code(uint32_t value) {
22 uint32_t cookiecode = (value >> 24) & 0xff;
23 cookiecode = cookiecode * 31 + ((value >> 16) & 0xff);
24 cookiecode = cookiecode * 31 + ((value >> 8) & 0xff);
25 cookiecode = cookiecode * 31 + ((value >> 0) & 0xff);
26 cookiecode &= (COOKIEMAP_ENTRIES-1);
27 return cookiecode * MAX_COLLISIONS;
28}
29
30static uint32_t gator_chksum_crc32(char *data)
31{
32 register unsigned long crc;
33 unsigned char *block = data;
34 int i, length = strlen(data);
35
36 crc = 0xFFFFFFFF;
37 for (i = 0; i < length; i++) {
38 crc = ((crc >> 8) & 0x00FFFFFF) ^ gator_crc32_table[(crc ^ *block++) & 0xFF];
39 }
40
41 return (crc ^ 0xFFFFFFFF);
42}
43
44/*
45 * Exists
46 * Pre: [0][1][v][3]..[n-1]
47 * Post: [v][0][1][3]..[n-1]
48 */
49static uint32_t cookiemap_exists(uint64_t key) {
50 int cpu = raw_smp_processor_id();
51 uint32_t cookiecode = cookiemap_code(key);
52 uint64_t *keys = &(per_cpu(cookie_keys, cpu)[cookiecode]);
53 uint32_t *values = &(per_cpu(cookie_values, cpu)[cookiecode]);
54 int x;
55
56 for (x = 0; x < MAX_COLLISIONS; x++) {
57 if (keys[x] == key) {
58 uint32_t value = values[x];
59 for (; x > 0; x--) {
60 keys[x] = keys[x-1];
61 values[x] = values[x-1];
62 }
63 keys[0] = key;
64 values[0] = value;
65 return value;
66 }
67 }
68
69 return 0;
70}
71
72/*
73 * Add
74 * Pre: [0][1][2][3]..[n-1]
75 * Post: [v][0][1][2]..[n-2]
76 */
77static void cookiemap_add(uint64_t key, uint32_t value) {
78 int cpu = raw_smp_processor_id();
79 int cookiecode = cookiemap_code(key);
80 uint64_t *keys = &(per_cpu(cookie_keys, cpu)[cookiecode]);
81 uint32_t *values = &(per_cpu(cookie_values, cpu)[cookiecode]);
82 int x;
83
84 for (x = MAX_COLLISIONS-1; x > 0; x--) {
85 keys[x] = keys[x-1];
86 values[x] = keys[x-1];
87 }
88 keys[0] = key;
89 values[0] = value;
90}
91
92static inline uint32_t get_cookie(int cpu, int tgid, struct vm_area_struct *vma)
93{
94 struct path *path;
95 uint64_t key;
96 int cookie;
97 char *text;
98
99 if (!vma || !vma->vm_file) {
100 return INVALID_COOKIE;
101 }
102 path = &vma->vm_file->f_path;
103 if (!path || !path->dentry) {
104 return INVALID_COOKIE;
105 }
106
107 text = (char*)path->dentry->d_name.name;
108 if (per_cpu (cookie_prev_text, cpu) == (uint32_t)text) {
109 return per_cpu(cookie_prev_value, cpu);
110 }
111
112 key = gator_chksum_crc32(text);
113 key = (key << 32) | (uint32_t)text;
114
115 cookie = cookiemap_exists(key);
116 if (cookie) {
117 goto output;
118 }
119
120 cookie = per_cpu(cookie_next_key, cpu)+=nr_cpu_ids;
121 cookiemap_add(key, cookie);
122
123 gator_buffer_write_packed_int(cpu, PROTOCOL_COOKIE);
124 gator_buffer_write_packed_int(cpu, cookie);
125 gator_buffer_write_string(cpu, text);
126
127output:
128 per_cpu(cookie_prev_text, cpu) = (uint32_t)text;
129 per_cpu(cookie_prev_value, cpu) = cookie;
130 return cookie;
131}
132
133static int get_exec_cookie(int cpu, struct task_struct *task)
134{
135 unsigned long cookie = NO_COOKIE;
136 struct mm_struct *mm = task->mm;
137 struct vm_area_struct *vma;
138
139 if (!mm)
140 return cookie;
141
142 for (vma = mm->mmap; vma; vma = vma->vm_next) {
143 if (!vma->vm_file)
144 continue;
145 if (!(vma->vm_flags & VM_EXECUTABLE))
146 continue;
147 cookie = get_cookie(cpu, task->tgid, vma);
148 break;
149 }
150
151 return cookie;
152}
153
154static unsigned long get_address_cookie(int cpu, struct task_struct *task, unsigned long addr, off_t *offset)
155{
156 unsigned long cookie = NO_COOKIE;
157 struct mm_struct *mm = task->mm;
158 struct vm_area_struct *vma;
159
160 if (!mm)
161 return cookie;
162
163 for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) {
164 if (addr < vma->vm_start || addr >= vma->vm_end)
165 continue;
166
167 if (vma->vm_file) {
168 cookie = get_cookie(cpu, task->tgid, vma);
169 *offset = (vma->vm_pgoff << PAGE_SHIFT) + addr - vma->vm_start;
170 } else {
171 /* must be an anonymous map */
172 *offset = addr;
173 }
174
175 break;
176 }
177
178 if (!vma)
179 cookie = INVALID_COOKIE;
180
181 return cookie;
182}
183
184static void cookies_initialize(void)
185{
186 uint32_t crc, poly;
187 int cpu, size;
188 int i, j;
189
190 for_each_possible_cpu(cpu) {
191 per_cpu(cookie_next_key, cpu) = nr_cpu_ids + cpu;
192 per_cpu(cookie_prev_text, cpu) = 0;
193 per_cpu(cookie_prev_value, cpu) = 0;
194
195 size = COOKIEMAP_ENTRIES * MAX_COLLISIONS * sizeof(uint64_t);
196 per_cpu(cookie_keys, cpu) = (uint64_t*)kmalloc(size, GFP_KERNEL);
197 memset(per_cpu(cookie_keys, cpu), 0, size);
198
199 size = COOKIEMAP_ENTRIES * MAX_COLLISIONS * sizeof(uint32_t);
200 per_cpu(cookie_values, cpu) = (uint32_t*)kmalloc(size, GFP_KERNEL);
201 memset(per_cpu(cookie_values, cpu), 0, size);
202 }
203
204 // build CRC32 table
205 poly = 0x04c11db7;
206 gator_crc32_table = (uint32_t*)kmalloc(256 * sizeof(uint32_t), GFP_KERNEL);
207 for (i = 0; i < 256; i++) {
208 crc = i;
209 for (j = 8; j > 0; j--) {
210 if (crc & 1) {
211 crc = (crc >> 1) ^ poly;
212 } else {
213 crc >>= 1;
214 }
215 }
216 gator_crc32_table[i] = crc;
217 }
218}
219
220static void cookies_release(void)
221{
222 int cpu;
223
224 for_each_possible_cpu(cpu) {
225 kfree(per_cpu(cookie_keys, cpu));
226 per_cpu(cookie_keys, cpu) = NULL;
227
228 kfree(per_cpu(cookie_values, cpu));
229 per_cpu(cookie_values, cpu) = NULL;
230 }
231
232 kfree(gator_crc32_table);
233 gator_crc32_table = NULL;
234}
diff --git a/gator_events.c b/gator_events.c
new file mode 100644
index 0000000..1a5d0a0
--- /dev/null
+++ b/gator_events.c
@@ -0,0 +1,37 @@
1/**
2 * Copyright (C) ARM Limited 2010. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10/**
11 * This file is #included in gator_main.c
12 * Update this file and Makefile to add custom counters.
13 */
14
15extern int gator_events_armv6_install(gator_interface *gi);
16extern int gator_events_armv7_install(gator_interface *gi);
17extern int gator_events_irq_install(gator_interface *gi);
18extern int gator_events_sched_install(gator_interface *gi);
19extern int gator_events_block_install(gator_interface *gi);
20extern int gator_events_meminfo_install(gator_interface *gi);
21
22static int gator_events_install(void)
23{
24 if (gator_event_install(gator_events_armv6_install))
25 return -1;
26 if (gator_event_install(gator_events_armv7_install))
27 return -1;
28 if (gator_event_install(gator_events_irq_install))
29 return -1;
30 if (gator_event_install(gator_events_sched_install))
31 return -1;
32 if (gator_event_install(gator_events_block_install))
33 return -1;
34 if (gator_event_install(gator_events_meminfo_install))
35 return -1;
36 return 0;
37}
diff --git a/gator_events.h b/gator_events.h
new file mode 100644
index 0000000..8237c1a
--- /dev/null
+++ b/gator_events.h
@@ -0,0 +1,19 @@
1/**
2 * Copyright 2010 ARM, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9
10struct __gator_interface {
11 int (*create_files)(struct super_block *sb, struct dentry *root);
12 int (*init)(int *key);
13 int (*start)(void);
14 void (*stop)(void);
15 int (*read)(int **buffer);
16 struct __gator_interface *next;
17};
18
19typedef struct __gator_interface gator_interface;
diff --git a/gator_events_armv6.c b/gator_events_armv6.c
new file mode 100644
index 0000000..a916d83
--- /dev/null
+++ b/gator_events_armv6.c
@@ -0,0 +1,250 @@
1/**
2 * Copyright (C) ARM Limited 2010. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include <linux/types.h>
10#include <linux/sched.h>
11#include <linux/interrupt.h>
12#include <linux/irq.h>
13#include <linux/fs.h>
14
15#include "gator.h"
16
17#if defined(__arm__)
18
19#define ARM1136 0xb36
20#define ARM1156 0xb56
21#define ARM1176 0xb76
22
23static const char *pmnc_name;
24
25extern u32 gator_cpuid(void);
26
27/*
28 * Per-CPU PMCR
29 */
30#define PMCR_E (1 << 0) /* Enable */
31#define PMCR_P (1 << 1) /* Count reset */
32#define PMCR_C (1 << 2) /* Cycle counter reset */
33#define PMCR_OFL_PMN0 (1 << 8) /* Count reg 0 overflow */
34#define PMCR_OFL_PMN1 (1 << 9) /* Count reg 1 overflow */
35#define PMCR_OFL_CCNT (1 << 10) /* Cycle counter overflow */
36
37#define PMN0 0
38#define PMN1 1
39#define CCNT 2
40#define CNTMAX (CCNT+1)
41
42static int pmnc_count = 0;
43static unsigned long pmnc_enabled[CNTMAX];
44static unsigned long pmnc_event[CNTMAX];
45static unsigned long pmnc_key[CNTMAX];
46
47static DEFINE_PER_CPU(int[CNTMAX], perfPrev);
48static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt);
49
50static inline void armv6_pmnc_write(u32 val)
51{
52 /* upper 4bits and 7, 11 are write-as-0 */
53 val &= 0x0ffff77f;
54 asm volatile("mcr p15, 0, %0, c15, c12, 0" : : "r" (val));
55}
56
57static inline u32 armv6_pmnc_read(void)
58{
59 u32 val;
60 asm volatile("mrc p15, 0, %0, c15, c12, 0" : "=r" (val));
61 return val;
62}
63
64static void armv6_pmnc_reset_counter(unsigned int cnt)
65{
66 u32 val = 0;
67 switch (cnt) {
68 case CCNT:
69 asm volatile("mcr p15, 0, %0, c15, c12, 1" : : "r" (val));
70 break;
71 case PMN0:
72 asm volatile("mcr p15, 0, %0, c15, c12, 2" : : "r" (val));
73 break;
74 case PMN1:
75 asm volatile("mcr p15, 0, %0, c15, c12, 3" : : "r" (val));
76 break;
77 }
78}
79
80int gator_events_armv6_create_files(struct super_block *sb, struct dentry *root)
81{
82 struct dentry *dir;
83 int i;
84
85 pmnc_count = 3;
86
87 for (i = PMN0; i <= CCNT; i++) {
88 char buf[40];
89 if (i == CCNT) {
90 snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name);
91 } else {
92 snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i);
93 }
94 dir = gatorfs_mkdir(sb, root, buf);
95 if (!dir) {
96 return -1;
97 }
98 gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]);
99 if (i != CCNT) {
100 gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]);
101 }
102 gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]);
103 }
104
105 return 0;
106}
107
108static int gator_events_armv6_init(int *key)
109{
110 unsigned int cnt;
111
112 for (cnt = PMN0; cnt <= CCNT; cnt++) {
113 pmnc_enabled[cnt] = 0;
114 pmnc_event[cnt] = 0;
115 pmnc_key[cnt] = *key;
116 *key = *key + 1;
117 }
118
119 return 0;
120}
121
122static void __gator_events_armv6_start(void* unused)
123{
124 unsigned int cnt;
125 u32 pmnc;
126
127 if (armv6_pmnc_read() & PMCR_E) {
128 pr_err("gator: CPU%u PMNC still enabled when setup new event counter.\n", smp_processor_id());
129 pmnc_count = 0;
130 return;
131 }
132
133 /* initialize PMNC, reset overflow, D bit, C bit and P bit. */
134 armv6_pmnc_write(PMCR_OFL_PMN0 | PMCR_OFL_PMN1 | PMCR_OFL_CCNT |
135 PMCR_C | PMCR_P);
136
137 /* configure control register */
138 for (pmnc = 0, cnt = PMN0; cnt <= CCNT; cnt++) {
139 unsigned long event;
140
141 per_cpu(perfPrev, raw_smp_processor_id())[cnt] = 0;
142
143 if (!pmnc_enabled[cnt])
144 continue;
145
146 event = pmnc_event[cnt] & 255;
147
148 /*
149 * Set event (if destined for PMNx counters)
150 */
151 if (cnt == PMN0) {
152 pmnc |= event << 20;
153 } else if (cnt == PMN1) {
154 pmnc |= event << 12;
155 }
156
157 /*
158 * Reset counter
159 */
160 armv6_pmnc_reset_counter(cnt);
161 }
162 armv6_pmnc_write(pmnc | PMCR_E);
163}
164
165static int gator_events_armv6_start(void)
166{
167 if (!pmnc_count)
168 return 0;
169 return on_each_cpu(__gator_events_armv6_start, NULL, 1);
170}
171
172static void __gator_events_armv6_stop(void* unused)
173{
174 unsigned int cnt;
175
176 armv6_pmnc_write(armv6_pmnc_read() & ~PMCR_E);
177
178 for (cnt = PMN0; cnt <= CCNT; cnt++) {
179 armv6_pmnc_reset_counter(cnt);
180 pmnc_enabled[cnt] = 0;
181 pmnc_event[cnt] = 0;
182 }
183}
184
185static void gator_events_armv6_stop(void)
186{
187 if (!pmnc_count)
188 return;
189 on_each_cpu(__gator_events_armv6_stop, NULL, 1);
190}
191
192static int gator_events_armv6_read(int **buffer)
193{
194 int cnt, len = 0;
195 int cpu = raw_smp_processor_id();
196
197 if (!pmnc_count)
198 return 0;
199
200 for (cnt = PMN0; cnt <= CCNT; cnt++) {
201 if (pmnc_enabled[cnt]) {
202 u32 value = 0;
203 switch (cnt) {
204 case CCNT:
205 asm volatile("mrc p15, 0, %0, c15, c12, 1" : "=r" (value));
206 break;
207 case PMN0:
208 asm volatile("mrc p15, 0, %0, c15, c12, 2" : "=r" (value));
209 break;
210 case PMN1:
211 asm volatile("mrc p15, 0, %0, c15, c12, 3" : "=r" (value));
212 break;
213 }
214 armv6_pmnc_reset_counter(cnt);
215 if (value != per_cpu(perfPrev, cpu)[cnt]) {
216 per_cpu(perfPrev, cpu)[cnt] = value;
217 per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
218 per_cpu(perfCnt, cpu)[len++] = value;
219 }
220 }
221 }
222
223 // update or discard
224 if (buffer)
225 *buffer = per_cpu(perfCnt, cpu);
226
227 return len;
228}
229#endif
230
231int gator_events_armv6_install(gator_interface *gi) {
232#if defined(__arm__)
233 switch (gator_cpuid()) {
234 case ARM1136:
235 case ARM1156:
236 case ARM1176:
237 pmnc_name = "ARM11";
238 break;
239 default:
240 return -1;
241 }
242
243 gi->create_files = gator_events_armv6_create_files;
244 gi->init = gator_events_armv6_init;
245 gi->start = gator_events_armv6_start;
246 gi->stop = gator_events_armv6_stop;
247 gi->read = gator_events_armv6_read;
248#endif
249 return 0;
250}
diff --git a/gator_events_armv7.c b/gator_events_armv7.c
new file mode 100644
index 0000000..ca37bef
--- /dev/null
+++ b/gator_events_armv7.c
@@ -0,0 +1,427 @@
1/**
2 * Copyright (C) ARM Limited 2010. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8#include <linux/types.h>
9#include <linux/sched.h>
10#include <linux/interrupt.h>
11#include <linux/irq.h>
12#include <linux/fs.h>
13
14#include "gator.h"
15
16#if defined(__arm__)
17
18#define CORTEX_A8 0xc08
19#define CORTEX_A9 0xc09
20
21static const char *pmnc_name;
22static int pmnc_count;
23
24extern u32 gator_cpuid(void);
25
26/*
27 * Per-CPU PMNC: config reg
28 */
29#define PMNC_E (1 << 0) /* Enable all counters */
30#define PMNC_P (1 << 1) /* Reset all counters */
31#define PMNC_C (1 << 2) /* Cycle counter reset */
32#define PMNC_D (1 << 3) /* CCNT counts every 64th cpu cycle */
33#define PMNC_X (1 << 4) /* Export to ETM */
34#define PMNC_DP (1 << 5) /* Disable CCNT if non-invasive debug*/
35#define PMNC_MASK 0x3f /* Mask for writable bits */
36
37/*
38 * CNTENS: counters enable reg
39 */
40#define CNTENS_P0 (1 << 0)
41#define CNTENS_P1 (1 << 1)
42#define CNTENS_P2 (1 << 2)
43#define CNTENS_P3 (1 << 3)
44#define CNTENS_C (1 << 31)
45#define CNTENS_MASK 0x8000000f /* Mask for writable bits */
46
47/*
48 * CNTENC: counters disable reg
49 */
50#define CNTENC_P0 (1 << 0)
51#define CNTENC_P1 (1 << 1)
52#define CNTENC_P2 (1 << 2)
53#define CNTENC_P3 (1 << 3)
54#define CNTENC_C (1 << 31)
55#define CNTENC_MASK 0x8000000f /* Mask for writable bits */
56
57/*
58 * INTENS: counters overflow interrupt enable reg
59 */
60#define INTENS_P0 (1 << 0)
61#define INTENS_P1 (1 << 1)
62#define INTENS_P2 (1 << 2)
63#define INTENS_P3 (1 << 3)
64#define INTENS_C (1 << 31)
65#define INTENS_MASK 0x8000000f /* Mask for writable bits */
66
67/*
68 * EVTSEL: Event selection reg
69 */
70#define EVTSEL_MASK 0x7f /* Mask for writable bits */
71
72/*
73 * SELECT: Counter selection reg
74 */
75#define SELECT_MASK 0x1f /* Mask for writable bits */
76
77/*
78 * FLAG: counters overflow flag status reg
79 */
80#define FLAG_P0 (1 << 0)
81#define FLAG_P1 (1 << 1)
82#define FLAG_P2 (1 << 2)
83#define FLAG_P3 (1 << 3)
84#define FLAG_C (1 << 31)
85#define FLAG_MASK 0x8000000f /* Mask for writable bits */
86
87#define CCNT 0
88#define CNT0 1
89#define CNTMAX (6+1)
90
91static unsigned long pmnc_enabled[CNTMAX];
92static unsigned long pmnc_event[CNTMAX];
93static unsigned long pmnc_key[CNTMAX];
94
95static DEFINE_PER_CPU(int[CNTMAX], perfPrev);
96static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt);
97
98static inline void armv7_pmnc_write(u32 val)
99{
100 val &= PMNC_MASK;
101 asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
102}
103
104static inline u32 armv7_pmnc_read(void)
105{
106 u32 val;
107 asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
108 return val;
109}
110
111static inline u32 armv7_ccnt_read(void)
112{
113 u32 val;
114 asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
115 return val;
116}
117
118static inline u32 armv7_cntn_read(void)
119{
120 u32 val;
121 asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val));
122 return val;
123}
124
125static inline u32 armv7_pmnc_enable_counter(unsigned int cnt)
126{
127 u32 val;
128
129 if (cnt >= CNTMAX) {
130 pr_err("gator: CPU%u enabling wrong PMNC counter %d\n", smp_processor_id(), cnt);
131 return -1;
132 }
133
134 if (cnt == CCNT)
135 val = CNTENS_C;
136 else
137 val = (1 << (cnt - CNT0));
138
139 val &= CNTENS_MASK;
140 asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
141
142 return cnt;
143}
144
145static inline u32 armv7_pmnc_disable_counter(unsigned int cnt)
146{
147 u32 val;
148
149 if (cnt >= CNTMAX) {
150 pr_err("gator: CPU%u disabling wrong PMNC counter %d\n", smp_processor_id(), cnt);
151 return -1;
152 }
153
154 if (cnt == CCNT)
155 val = CNTENC_C;
156 else
157 val = (1 << (cnt - CNT0));
158
159 val &= CNTENC_MASK;
160 asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
161
162 return cnt;
163}
164
165static inline u32 armv7_pmnc_enable_intens(unsigned int cnt)
166{
167 u32 val;
168
169 if (cnt >= CNTMAX) {
170 pr_err("gator: CPU%u enabling wrong PMNC counter interrupt enable %d\n", smp_processor_id(), cnt);
171 return -1;
172 }
173
174 if (cnt == CCNT)
175 val = INTENS_C;
176 else
177 val = (1 << (cnt - CNT0));
178
179 val &= INTENS_MASK;
180 asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (val));
181
182 return cnt;
183}
184
185static inline u32 armv7_pmnc_getreset_flags(void)
186{
187 u32 val;
188
189 /* Read */
190 asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
191
192 /* Write to clear flags */
193 val &= FLAG_MASK;
194 asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (val));
195
196 return val;
197}
198
199static inline int armv7_pmnc_select_counter(unsigned int cnt)
200{
201 u32 val;
202
203 if ((cnt == CCNT) || (cnt >= CNTMAX)) {
204 pr_err("gator: CPU%u selecting wrong PMNC counter %d\n", smp_processor_id(), cnt);
205 return -1;
206 }
207
208 val = (cnt - CNT0) & SELECT_MASK;
209 asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
210
211 return cnt;
212}
213
214static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val)
215{
216 if (armv7_pmnc_select_counter(cnt) == cnt) {
217 val &= EVTSEL_MASK;
218 asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
219 }
220}
221
222static void armv7_pmnc_reset_counter(unsigned int cnt)
223{
224 u32 val = 0;
225
226 if (cnt == CCNT) {
227 armv7_pmnc_disable_counter(cnt);
228
229 asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val));
230
231 if (pmnc_enabled[cnt] != 0)
232 armv7_pmnc_enable_counter(cnt);
233
234 } else if (cnt >= CNTMAX) {
235 pr_err("gator: CPU%u resetting wrong PMNC counter %d\n", smp_processor_id(), cnt);
236 } else {
237 armv7_pmnc_disable_counter(cnt);
238
239 if (armv7_pmnc_select_counter(cnt) == cnt)
240 asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val));
241
242 if (pmnc_enabled[cnt] != 0)
243 armv7_pmnc_enable_counter(cnt);
244 }
245}
246
247static int gator_events_armv7_create_files(struct super_block *sb, struct dentry *root)
248{
249 struct dentry *dir;
250 int i;
251
252 for (i = 0; i < pmnc_count; i++) {
253 char buf[40];
254 if (i == 0) {
255 snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name);
256 } else {
257 snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i-1);
258 }
259 dir = gatorfs_mkdir(sb, root, buf);
260 if (!dir) {
261 return -1;
262 }
263 gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]);
264 if (i > 0) {
265 gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]);
266 }
267 gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]);
268 }
269
270 return 0;
271}
272
273static int gator_events_armv7_init(int *key)
274{
275 unsigned int cnt;
276
277 for (cnt = CCNT; cnt < CNTMAX; cnt++) {
278 pmnc_enabled[cnt] = 0;
279 pmnc_event[cnt] = 0;
280 pmnc_key[cnt] = *key;
281 *key = *key + 1;
282 }
283
284 return 0;
285}
286
287static void __gator_events_armv7_start(void* unused)
288{
289 unsigned int cnt;
290
291 if (armv7_pmnc_read() & PMNC_E) {
292 pr_err("gator: CPU%u PMNC still enabled when setup new event counter\n", raw_smp_processor_id());
293 pmnc_count = 0;
294 return;
295 }
296
297 /* Initialize & Reset PMNC: C bit and P bit */
298 armv7_pmnc_write(PMNC_P | PMNC_C);
299
300 for (cnt = CCNT; cnt < CNTMAX; cnt++) {
301 unsigned long event;
302
303 per_cpu(perfPrev, raw_smp_processor_id())[cnt] = 0;
304
305 if (!pmnc_enabled[cnt])
306 continue;
307
308 /*
309 * Disable counter
310 */
311 armv7_pmnc_disable_counter(cnt);
312
313 event = pmnc_event[cnt] & 255;
314
315 /*
316 * Set event (if destined for PMNx counters)
317 * We don't need to set the event if it's a cycle count
318 */
319 if (cnt != CCNT)
320 armv7_pmnc_write_evtsel(cnt, event);
321
322 /*
323 * [Do not] Enable interrupt for this counter
324 */
325 /* armv7_pmnc_enable_intens(cnt); */
326
327 /*
328 * Reset counter
329 */
330 armv7_pmnc_reset_counter(cnt);
331
332 /*
333 * Enable counter
334 */
335 armv7_pmnc_enable_counter(cnt);
336 }
337
338 // enable
339 armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
340}
341
342static int gator_events_armv7_start(void)
343{
344 if (!pmnc_count)
345 return 0;
346 return on_each_cpu(__gator_events_armv7_start, NULL, 1);
347}
348
349static void __gator_events_armv7_stop(void* unused)
350{
351 unsigned int cnt;
352 armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
353
354 for (cnt = CCNT; cnt < CNTMAX; cnt++) {
355 pmnc_enabled[cnt] = 0;
356 pmnc_event[cnt] = 0;
357 }
358}
359
360static void gator_events_armv7_stop(void)
361{
362 if (!pmnc_count)
363 return;
364 on_each_cpu(__gator_events_armv7_stop, NULL, 1);
365}
366
367static int gator_events_armv7_read(int **buffer)
368{
369 int cnt, len = 0;
370 int cpu = raw_smp_processor_id();
371
372 if (!pmnc_count)
373 return 0;
374
375 armv7_pmnc_getreset_flags();
376 for (cnt = 0; cnt < pmnc_count; cnt++) {
377 if (pmnc_enabled[cnt]) {
378 int value;
379 if (cnt == CCNT) {
380 value = armv7_ccnt_read();
381 } else if (armv7_pmnc_select_counter(cnt) == cnt) {
382 value = armv7_cntn_read();
383 } else {
384 value = 0;
385 }
386 armv7_pmnc_reset_counter(cnt);
387 if (value != per_cpu(perfPrev, cpu)[cnt]) {
388 per_cpu(perfPrev, cpu)[cnt] = value;
389 per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
390 per_cpu(perfCnt, cpu)[len++] = value;
391 }
392 }
393 }
394
395 // update or discard
396 if (buffer)
397 *buffer = per_cpu(perfCnt, cpu);
398
399 return len;
400}
401#endif
402
403int gator_events_armv7_install(gator_interface *gi) {
404#if defined(__arm__)
405 switch (gator_cpuid()) {
406 case CORTEX_A8:
407 pmnc_name = "Cortex-A8";
408 pmnc_count = 4;
409 break;
410 case CORTEX_A9:
411 pmnc_name = "Cortex-A9";
412 pmnc_count = 6;
413 break;
414 default:
415 return -1;
416 }
417
418 pmnc_count++; // CNT[n] + CCNT
419
420 gi->create_files = gator_events_armv7_create_files;
421 gi->init = gator_events_armv7_init;
422 gi->start = gator_events_armv7_start;
423 gi->stop = gator_events_armv7_stop;
424 gi->read = gator_events_armv7_read;
425#endif
426 return 0;
427}
diff --git a/gator_events_block.c b/gator_events_block.c
new file mode 100644
index 0000000..c2d1e7e
--- /dev/null
+++ b/gator_events_block.c
@@ -0,0 +1,180 @@
1/**
2 * Copyright (C) ARM Limited 2010. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10#include <linux/version.h>
11#include <linux/blkdev.h>
12#include <trace/events/block.h>
13#include <linux/slab.h>
14#include <linux/fs.h>
15
16#include "gator.h"
17#include "gator_trace.h"
18
19#define BLOCK_RQ_WR 0
20#define BLOCK_RQ_RD 1
21
22#define BLOCK_TOTAL (BLOCK_RQ_RD+1)
23
24#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
25#define EVENTWRITE REQ_RW
26#else
27#define EVENTWRITE REQ_WRITE
28#endif
29
30static ulong block_rq_wr_enabled;
31static ulong block_rq_rd_enabled;
32static ulong block_rq_wr_key;
33static ulong block_rq_rd_key;
34static DEFINE_PER_CPU(int[BLOCK_TOTAL], blockCnt);
35static DEFINE_PER_CPU(int[BLOCK_TOTAL * 2], blockGet);
36static DEFINE_PER_CPU(bool, new_data_avail);
37
38GATOR_DEFINE_PROBE(block_rq_complete, TP_PROTO(struct request_queue *q, struct request *rq))
39{
40 unsigned long flags;
41 int write, size;
42 int cpu = raw_smp_processor_id();
43
44 if (!rq)
45 return;
46
47 write = rq->cmd_flags & EVENTWRITE;
48 size = rq->resid_len;
49
50 if (!size)
51 return;
52
53 // disable interrupts to synchronize with gator_events_block_read()
54 // spinlocks not needed since percpu buffers are used
55 local_irq_save(flags);
56 if (write)
57 per_cpu(blockCnt, cpu)[BLOCK_RQ_WR] += size;
58 else
59 per_cpu(blockCnt, cpu)[BLOCK_RQ_RD] += size;
60 local_irq_restore(flags);
61
62 per_cpu(new_data_avail, cpu) = true;
63}
64
65static int gator_events_block_create_files(struct super_block *sb, struct dentry *root)
66{
67 struct dentry *dir;
68
69 /* block_complete_wr */
70 dir = gatorfs_mkdir(sb, root, "Linux_block_rq_wr");
71 if (!dir) {
72 return -1;
73 }
74 gatorfs_create_ulong(sb, dir, "enabled", &block_rq_wr_enabled);
75 gatorfs_create_ro_ulong(sb, dir, "key", &block_rq_wr_key);
76
77 /* block_complete_rd */
78 dir = gatorfs_mkdir(sb, root, "Linux_block_rq_rd");
79 if (!dir) {
80 return -1;
81 }
82 gatorfs_create_ulong(sb, dir, "enabled", &block_rq_rd_enabled);
83 gatorfs_create_ro_ulong(sb, dir, "key", &block_rq_rd_key);
84
85 return 0;
86}
87
88static int gator_events_block_init(int *key)
89{
90 block_rq_wr_enabled = 0;
91 block_rq_rd_enabled = 0;
92
93 block_rq_wr_key = *key;
94 *key = *key + 1;
95 block_rq_rd_key = *key;
96 *key = *key + 1;
97
98 return 0;
99}
100
101static int gator_events_block_start(void)
102{
103 int cpu;
104
105 for_each_possible_cpu(cpu)
106 per_cpu(new_data_avail, cpu) = true;
107
108 // register tracepoints
109 if (block_rq_wr_enabled || block_rq_rd_enabled)
110 if (GATOR_REGISTER_TRACE(block_rq_complete))
111 goto fail_block_rq_exit;
112 pr_debug("gator: registered block event tracepoints\n");
113
114 return 0;
115
116 // unregister tracepoints on error
117fail_block_rq_exit:
118 pr_err("gator: block event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
119
120 return -1;
121}
122
123static void gator_events_block_stop(void)
124{
125 if (block_rq_wr_enabled || block_rq_rd_enabled)
126 GATOR_UNREGISTER_TRACE(block_rq_complete);
127 pr_debug("gator: unregistered block event tracepoints\n");
128
129 block_rq_wr_enabled = 0;
130 block_rq_rd_enabled = 0;
131}
132
133static int gator_events_block_read(int **buffer)
134{
135 unsigned long flags;
136 int len, value, cpu, data = 0;
137 cpu = raw_smp_processor_id();
138
139 if (per_cpu(new_data_avail, cpu) == false)
140 return 0;
141
142 per_cpu(new_data_avail, cpu) = false;
143
144 len = 0;
145 if (block_rq_wr_enabled) {
146 local_irq_save(flags);
147 value = per_cpu(blockCnt, cpu)[BLOCK_RQ_WR];
148 per_cpu(blockCnt, cpu)[BLOCK_RQ_WR] = 0;
149 local_irq_restore(flags);
150 per_cpu(blockGet, cpu)[len++] = block_rq_wr_key;
151 per_cpu(blockGet, cpu)[len++] = value;
152 data += value;
153 }
154 if (block_rq_rd_enabled) {
155 local_irq_save(flags);
156 value = per_cpu(blockCnt, cpu)[BLOCK_RQ_RD];
157 per_cpu(blockCnt, cpu)[BLOCK_RQ_RD] = 0;
158 local_irq_restore(flags);
159 per_cpu(blockGet, cpu)[len++] = block_rq_rd_key;
160 per_cpu(blockGet, cpu)[len++] = value;
161 data += value;
162 }
163
164 if (data != 0)
165 per_cpu(new_data_avail, cpu) = true;
166
167 if (buffer)
168 *buffer = per_cpu(blockGet, cpu);
169
170 return len;
171}
172
173int gator_events_block_install(gator_interface *gi) {
174 gi->create_files = gator_events_block_create_files;
175 gi->init = gator_events_block_init;
176 gi->start = gator_events_block_start;
177 gi->stop = gator_events_block_stop;
178 gi->read = gator_events_block_read;
179 return 0;
180}
diff --git a/gator_events_irq.c b/gator_events_irq.c
new file mode 100644
index 0000000..f34cc18
--- /dev/null
+++ b/gator_events_irq.c
@@ -0,0 +1,178 @@
1/**
2 * Copyright (C) ARM Limited 2010. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10#include <linux/slab.h>
11#include <linux/fs.h>
12#include <linux/mm.h>
13#include <linux/dcookies.h>
14#include <linux/version.h>
15#include <trace/events/irq.h>
16
17#include "gator.h"
18#include "gator_trace.h"
19
20#define HARDIRQ 0
21#define SOFTIRQ 1
22#define TOTALIRQ (SOFTIRQ+1)
23
24static ulong hardirq_enabled;
25static ulong softirq_enabled;
26static ulong hardirq_key;
27static ulong softirq_key;
28static DEFINE_PER_CPU(int[TOTALIRQ], irqCnt);
29static DEFINE_PER_CPU(int[TOTALIRQ], irqPrev);
30static DEFINE_PER_CPU(int[TOTALIRQ * 2], irqGet);
31
32GATOR_DEFINE_PROBE(irq_handler_exit, TP_PROTO(int irq,
33 struct irqaction *action, int ret))
34{
35 unsigned long flags;
36
37 // disable interrupts to synchronize with gator_events_irq_read()
38 // spinlocks not needed since percpu buffers are used
39 local_irq_save(flags);
40 per_cpu(irqCnt, raw_smp_processor_id())[HARDIRQ]++;
41 local_irq_restore(flags);
42}
43
44GATOR_DEFINE_PROBE(softirq_exit, TP_PROTO(struct softirq_action *h,
45 struct softirq_action *vec))
46{
47 unsigned long flags;
48
49 // disable interrupts to synchronize with gator_events_irq_read()
50 // spinlocks not needed since percpu buffers are used
51 local_irq_save(flags);
52 per_cpu(irqCnt, raw_smp_processor_id())[SOFTIRQ]++;
53 local_irq_restore(flags);
54}
55
56static int gator_events_irq_create_files(struct super_block *sb, struct dentry *root)
57{
58 struct dentry *dir;
59
60 /* irq */
61 dir = gatorfs_mkdir(sb, root, "Linux_irq_irq");
62 if (!dir) {
63 return -1;
64 }
65 gatorfs_create_ulong(sb, dir, "enabled", &hardirq_enabled);
66 gatorfs_create_ro_ulong(sb, dir, "key", &hardirq_key);
67
68 /* soft irq */
69 dir = gatorfs_mkdir(sb, root, "Linux_irq_softirq");
70 if (!dir) {
71 return -1;
72 }
73 gatorfs_create_ulong(sb, dir, "enabled", &softirq_enabled);
74 gatorfs_create_ro_ulong(sb, dir, "key", &softirq_key);
75
76 return 0;
77}
78
79static int gator_events_irq_init(int *key)
80{
81 hardirq_key = *key;
82 *key = *key + 1;
83 softirq_key = *key;
84 *key = *key + 1;
85
86 hardirq_enabled = 0;
87 softirq_enabled = 0;
88
89 return 0;
90}
91
92static int gator_events_irq_start(void)
93{
94 int cpu, i;
95
96 for_each_possible_cpu(cpu) {
97 for (i = 0; i < TOTALIRQ; i++)
98 per_cpu(irqPrev, cpu)[i] = 0;
99 }
100
101 // register tracepoints
102 if (hardirq_enabled)
103 if (GATOR_REGISTER_TRACE(irq_handler_exit))
104 goto fail_hardirq_exit;
105 if (softirq_enabled)
106 if (GATOR_REGISTER_TRACE(softirq_exit))
107 goto fail_softirq_exit;
108 pr_debug("gator: registered irq tracepoints\n");
109
110 return 0;
111
112 // unregister tracepoints on error
113fail_softirq_exit:
114 if (hardirq_enabled)
115 GATOR_UNREGISTER_TRACE(irq_handler_exit);
116fail_hardirq_exit:
117 pr_err("gator: irq tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
118
119 return -1;
120}
121
122static void gator_events_irq_stop(void)
123{
124 if (hardirq_enabled)
125 GATOR_UNREGISTER_TRACE(irq_handler_exit);
126 if (softirq_enabled)
127 GATOR_UNREGISTER_TRACE(softirq_exit);
128 pr_debug("gator: unregistered irq tracepoints\n");
129
130 hardirq_enabled = 0;
131 softirq_enabled = 0;
132}
133
134static int gator_events_irq_read(int **buffer)
135{
136 unsigned long flags;
137 int len, value;
138 int cpu = raw_smp_processor_id();
139
140 len = 0;
141 if (hardirq_enabled) {
142 local_irq_save(flags);
143 value = per_cpu(irqCnt, cpu)[HARDIRQ];
144 per_cpu(irqCnt, cpu)[HARDIRQ] = 0;
145 local_irq_restore(flags);
146 if (value != per_cpu(irqPrev, cpu)[HARDIRQ]) {
147 per_cpu(irqPrev, cpu)[HARDIRQ] = value;
148 per_cpu(irqGet, cpu)[len++] = hardirq_key;
149 per_cpu(irqGet, cpu)[len++] = value;
150 }
151 }
152
153 if (softirq_enabled) {
154 local_irq_save(flags);
155 value = per_cpu(irqCnt, cpu)[SOFTIRQ];
156 per_cpu(irqCnt, cpu)[SOFTIRQ] = 0;
157 local_irq_restore(flags);
158 if (value != per_cpu(irqPrev, cpu)[SOFTIRQ]) {
159 per_cpu(irqPrev, cpu)[SOFTIRQ] = value;
160 per_cpu(irqGet, cpu)[len++] = softirq_key;
161 per_cpu(irqGet, cpu)[len++] = value;
162 }
163 }
164
165 if (buffer)
166 *buffer = per_cpu(irqGet, cpu);
167
168 return len;
169}
170
171int gator_events_irq_install(gator_interface *gi) {
172 gi->create_files = gator_events_irq_create_files;
173 gi->init = gator_events_irq_init;
174 gi->start = gator_events_irq_start;
175 gi->stop = gator_events_irq_stop;
176 gi->read = gator_events_irq_read;
177 return 0;
178}
diff --git a/gator_events_meminfo.c b/gator_events_meminfo.c
new file mode 100644
index 0000000..52345c8
--- /dev/null
+++ b/gator_events_meminfo.c
@@ -0,0 +1,203 @@
1/**
2 * Copyright (C) ARM Limited 2010. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10#include <linux/workqueue.h>
11#include <linux/version.h>
12#include <linux/slab.h>
13#include <linux/mm.h>
14#include <linux/fs.h>
15#include <trace/events/kmem.h>
16
17#include "gator.h"
18#include "gator_trace.h"
19
20#define MEMINFO_MEMFREE 0
21#define MEMINFO_MEMUSED 1
22#define MEMINFO_BUFFERRAM 2
23#define MEMINFO_TOTAL 3
24
25static ulong meminfo_global_enabled;
26static ulong meminfo_enabled[MEMINFO_TOTAL];
27static ulong meminfo_key[MEMINFO_TOTAL];
28static int meminfo_buffer[MEMINFO_TOTAL * 2];
29static int meminfo_length = 0;
30static unsigned int mem_event = 0;
31static bool new_data_avail;
32
33static void wq_sched_handler(struct work_struct *wsptr);
34
35DECLARE_WORK(work, wq_sched_handler);
36
37GATOR_DEFINE_PROBE(mm_page_free_direct, TP_PROTO(struct page *page, unsigned int order)) {
38 mem_event++;
39}
40
41GATOR_DEFINE_PROBE(mm_pagevec_free, TP_PROTO(struct page *page, int cold)) {
42 mem_event++;
43}
44
45GATOR_DEFINE_PROBE(mm_page_alloc, TP_PROTO(struct page *page, unsigned int order, gfp_t gfp_flags, int migratetype)) {
46 mem_event++;
47}
48
49static int gator_events_meminfo_create_files(struct super_block *sb, struct dentry *root)
50{
51 struct dentry *dir;
52 int i;
53
54 for (i = 0; i < MEMINFO_TOTAL; i++) {
55 switch (i) {
56 case MEMINFO_MEMFREE:
57 dir = gatorfs_mkdir(sb, root, "Linux_meminfo_memfree");
58 break;
59 case MEMINFO_MEMUSED:
60 dir = gatorfs_mkdir(sb, root, "Linux_meminfo_memused");
61 break;
62 case MEMINFO_BUFFERRAM:
63 dir = gatorfs_mkdir(sb, root, "Linux_meminfo_bufferram");
64 break;
65 default:
66 return -1;
67 }
68 if (!dir) {
69 return -1;
70 }
71 gatorfs_create_ulong(sb, dir, "enabled", &meminfo_enabled[i]);
72 gatorfs_create_ro_ulong(sb, dir, "key", &meminfo_key[i]);
73 }
74
75 return 0;
76}
77
78static int gator_events_meminfo_init(int *key)
79{
80 int i;
81
82 meminfo_global_enabled = 0;
83 for (i = 0; i < MEMINFO_TOTAL; i++) {
84 meminfo_enabled[i] = 0;
85 meminfo_key[i] = *key;
86 *key = *key + 1;
87 }
88
89 return 0;
90}
91
92static int gator_events_meminfo_start(void)
93{
94 int i;
95
96 new_data_avail = true;
97 for (i = 0; i < MEMINFO_TOTAL; i++) {
98 if (meminfo_enabled[i]) {
99 meminfo_global_enabled = 1;
100 }
101 }
102
103 if (meminfo_global_enabled == 0)
104 return 0;
105
106 if (GATOR_REGISTER_TRACE(mm_page_free_direct))
107 goto mm_page_free_direct_exit;
108 if (GATOR_REGISTER_TRACE(mm_pagevec_free))
109 goto mm_pagevec_free_exit;
110 if (GATOR_REGISTER_TRACE(mm_page_alloc))
111 goto mm_page_alloc_exit;
112
113 return 0;
114
115mm_page_alloc_exit:
116 GATOR_UNREGISTER_TRACE(mm_pagevec_free);
117mm_pagevec_free_exit:
118 GATOR_UNREGISTER_TRACE(mm_page_free_direct);
119mm_page_free_direct_exit:
120 return -1;
121}
122
123static void gator_events_meminfo_stop(void)
124{
125 int i;
126
127 if (meminfo_global_enabled) {
128 GATOR_UNREGISTER_TRACE(mm_page_free_direct);
129 GATOR_UNREGISTER_TRACE(mm_pagevec_free);
130 GATOR_UNREGISTER_TRACE(mm_page_alloc);
131 }
132
133 meminfo_global_enabled = 0;
134 for (i = 0; i < MEMINFO_TOTAL; i++) {
135 meminfo_enabled[i] = 0;
136 }
137}
138
139// Must be run in a work queue as the kernel function si_meminfo() can sleep
140static void wq_sched_handler(struct work_struct *wsptr)
141{
142 struct sysinfo info;
143 int i, len, value;
144
145 meminfo_length = len = 0;
146
147 si_meminfo(&info);
148 for (i = 0; i < MEMINFO_TOTAL; i++) {
149 if (meminfo_enabled[i]) {
150 switch (i) {
151 case MEMINFO_MEMFREE:
152 value = info.freeram * PAGE_SIZE;
153 break;
154 case MEMINFO_MEMUSED:
155 value = (info.totalram - info.freeram) * PAGE_SIZE;
156 break;
157 case MEMINFO_BUFFERRAM:
158 value = info.bufferram * PAGE_SIZE;
159 break;
160 default:
161 value = 0;
162 break;
163 }
164 meminfo_buffer[len++] = meminfo_key[i];
165 meminfo_buffer[len++] = value;
166 }
167 }
168
169 meminfo_length = len;
170 new_data_avail = true;
171}
172
173static int gator_events_meminfo_read(int **buffer)
174{
175 static unsigned int last_mem_event = 0;
176
177 if (smp_processor_id() || !meminfo_global_enabled)
178 return 0;
179
180 if (last_mem_event != mem_event) {
181 last_mem_event = mem_event;
182 schedule_work(&work);
183 }
184
185 if (!new_data_avail)
186 return 0;
187
188 new_data_avail = false;
189
190 if (buffer)
191 *buffer = meminfo_buffer;
192
193 return meminfo_length;
194}
195
196int gator_events_meminfo_install(gator_interface *gi) {
197 gi->create_files = gator_events_meminfo_create_files;
198 gi->init = gator_events_meminfo_init;
199 gi->start = gator_events_meminfo_start;
200 gi->stop = gator_events_meminfo_stop;
201 gi->read = gator_events_meminfo_read;
202 return 0;
203}
diff --git a/gator_events_sched.c b/gator_events_sched.c
new file mode 100644
index 0000000..57b710a
--- /dev/null
+++ b/gator_events_sched.c
@@ -0,0 +1,123 @@
1/**
2 * Copyright (C) ARM Limited 2010. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10#include <linux/version.h>
11#include <linux/slab.h>
12#include <linux/fs.h>
13#include <linux/mm.h>
14#include <linux/dcookies.h>
15#include <trace/events/sched.h>
16
17#include "gator.h"
18#include "gator_trace.h"
19
20#define SCHED_SWITCH 0
21#define SCHED_TOTAL (SCHED_SWITCH+1)
22
23static ulong sched_switch_enabled;
24static ulong sched_switch_key;
25static DEFINE_PER_CPU(int[SCHED_TOTAL], schedCnt);
26static DEFINE_PER_CPU(int[SCHED_TOTAL * 2], schedGet);
27
28#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
29GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next))
30#else
31GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next))
32#endif
33{
34 unsigned long flags;
35
36 // disable interrupts to synchronize with gator_events_sched_read()
37 // spinlocks not needed since percpu buffers are used
38 local_irq_save(flags);
39 per_cpu(schedCnt, raw_smp_processor_id())[SCHED_SWITCH]++;
40 local_irq_restore(flags);
41}
42
43static int gator_events_sched_create_files(struct super_block *sb, struct dentry *root)
44{
45 struct dentry *dir;
46
47 /* switch */
48 dir = gatorfs_mkdir(sb, root, "Linux_sched_switch");
49 if (!dir) {
50 return -1;
51 }
52 gatorfs_create_ulong(sb, dir, "enabled", &sched_switch_enabled);
53 gatorfs_create_ro_ulong(sb, dir, "key", &sched_switch_key);
54
55 return 0;
56}
57
58static int gator_events_sched_init(int *key)
59{
60 sched_switch_enabled = 0;
61
62 sched_switch_key = *key;
63 *key = *key + 1;
64
65 return 0;
66}
67
68static int gator_events_sched_start(void)
69{
70 // register tracepoints
71 if (sched_switch_enabled)
72 if (GATOR_REGISTER_TRACE(sched_switch))
73 goto sched_switch_exit;
74 pr_debug("gator: registered scheduler event tracepoints\n");
75
76 return 0;
77
78 // unregister tracepoints on error
79sched_switch_exit:
80 pr_err("gator: scheduler event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
81
82 return -1;
83}
84
85static void gator_events_sched_stop(void)
86{
87 if (sched_switch_enabled)
88 GATOR_UNREGISTER_TRACE(sched_switch);
89 pr_debug("gator: unregistered scheduler event tracepoints\n");
90
91 sched_switch_enabled = 0;
92}
93
94static int gator_events_sched_read(int **buffer)
95{
96 unsigned long flags;
97 int len, value;
98 int cpu = raw_smp_processor_id();
99
100 len = 0;
101 if (sched_switch_enabled) {
102 local_irq_save(flags);
103 value = per_cpu(schedCnt, cpu)[SCHED_SWITCH];
104 per_cpu(schedCnt, cpu)[SCHED_SWITCH] = 0;
105 local_irq_restore(flags);
106 per_cpu(schedGet, cpu)[len++] = sched_switch_key;
107 per_cpu(schedGet, cpu)[len++] = value;
108 }
109
110 if (buffer)
111 *buffer = per_cpu(schedGet, cpu);
112
113 return len;
114}
115
116int gator_events_sched_install(gator_interface *gi) {
117 gi->create_files = gator_events_sched_create_files;
118 gi->init = gator_events_sched_init;
119 gi->start = gator_events_sched_start;
120 gi->stop = gator_events_sched_stop;
121 gi->read = gator_events_sched_read;
122 return 0;
123}
diff --git a/gator_fs.c b/gator_fs.c
new file mode 100644
index 0000000..5f8983f
--- /dev/null
+++ b/gator_fs.c
@@ -0,0 +1,270 @@
1/**
2 * @file gatorfs.c
3 *
4 * @remark Copyright 2002 OProfile authors
5 * @remark Read the file COPYING
6 *
7 * @author John Levon
8 *
9 * A simple filesystem for configuration and
10 * access of oprofile.
11 */
12
13#include <linux/init.h>
14#include <linux/module.h>
15#include <linux/fs.h>
16#include <linux/pagemap.h>
17#include <asm/uaccess.h>
18
19#define gatorfs_MAGIC 0x24051020
20#define TMPBUFSIZE 50
21DEFINE_SPINLOCK(gatorfs_lock);
22
23void gator_op_create_files(struct super_block *sb, struct dentry *root);
24
25static struct inode *gatorfs_get_inode(struct super_block *sb, int mode)
26{
27 struct inode *inode = new_inode(sb);
28
29 if (inode) {
30 inode->i_mode = mode;
31 inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
32 }
33 return inode;
34}
35
36static const struct super_operations s_ops = {
37 .statfs = simple_statfs,
38 .drop_inode = generic_delete_inode,
39};
40
41ssize_t gatorfs_str_to_user(char const *str, char __user *buf, size_t count, loff_t *offset)
42{
43 return simple_read_from_buffer(buf, count, offset, str, strlen(str));
44}
45
46ssize_t gatorfs_ulong_to_user(unsigned long val, char __user *buf, size_t count, loff_t *offset)
47{
48 char tmpbuf[TMPBUFSIZE];
49 size_t maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%lu\n", val);
50 if (maxlen > TMPBUFSIZE)
51 maxlen = TMPBUFSIZE;
52 return simple_read_from_buffer(buf, count, offset, tmpbuf, maxlen);
53}
54
55int gatorfs_ulong_from_user(unsigned long *val, char const __user *buf, size_t count)
56{
57 char tmpbuf[TMPBUFSIZE];
58 unsigned long flags;
59
60 if (!count)
61 return 0;
62
63 if (count > TMPBUFSIZE - 1)
64 return -EINVAL;
65
66 memset(tmpbuf, 0x0, TMPBUFSIZE);
67
68 if (copy_from_user(tmpbuf, buf, count))
69 return -EFAULT;
70
71 spin_lock_irqsave(&gatorfs_lock, flags);
72 *val = simple_strtoul(tmpbuf, NULL, 0);
73 spin_unlock_irqrestore(&gatorfs_lock, flags);
74 return 0;
75}
76
77static ssize_t ulong_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset)
78{
79 unsigned long *val = file->private_data;
80 return gatorfs_ulong_to_user(*val, buf, count, offset);
81}
82
83static ssize_t ulong_write_file(struct file *file, char const __user *buf, size_t count, loff_t *offset)
84{
85 unsigned long *value = file->private_data;
86 int retval;
87
88 if (*offset)
89 return -EINVAL;
90
91 retval = gatorfs_ulong_from_user(value, buf, count);
92
93 if (retval)
94 return retval;
95 return count;
96}
97
98static int default_open(struct inode *inode, struct file *filp)
99{
100 if (inode->i_private)
101 filp->private_data = inode->i_private;
102 return 0;
103}
104
105static const struct file_operations ulong_fops = {
106 .read = ulong_read_file,
107 .write = ulong_write_file,
108 .open = default_open,
109};
110
111static const struct file_operations ulong_ro_fops = {
112 .read = ulong_read_file,
113 .open = default_open,
114};
115
116static struct dentry *__gatorfs_create_file(struct super_block *sb,
117 struct dentry *root, char const *name, const struct file_operations *fops,
118 int perm)
119{
120 struct dentry *dentry;
121 struct inode *inode;
122
123 dentry = d_alloc_name(root, name);
124 if (!dentry)
125 return NULL;
126 inode = gatorfs_get_inode(sb, S_IFREG | perm);
127 if (!inode) {
128 dput(dentry);
129 return NULL;
130 }
131 inode->i_fop = fops;
132 d_add(dentry, inode);
133 return dentry;
134}
135
136int gatorfs_create_ulong(struct super_block *sb, struct dentry *root,
137 char const *name, unsigned long *val)
138{
139 struct dentry *d = __gatorfs_create_file(sb, root, name,
140 &ulong_fops, 0644);
141 if (!d)
142 return -EFAULT;
143
144 d->d_inode->i_private = val;
145 return 0;
146}
147
148int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root,
149 char const *name, unsigned long *val)
150{
151 struct dentry *d = __gatorfs_create_file(sb, root, name,
152 &ulong_ro_fops, 0444);
153 if (!d)
154 return -EFAULT;
155
156 d->d_inode->i_private = val;
157 return 0;
158}
159
160static ssize_t atomic_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset)
161{
162 atomic_t *val = file->private_data;
163 return gatorfs_ulong_to_user(atomic_read(val), buf, count, offset);
164}
165
166static const struct file_operations atomic_ro_fops = {
167 .read = atomic_read_file,
168 .open = default_open,
169};
170
171int gatorfs_create_ro_atomic(struct super_block *sb, struct dentry *root,
172 char const *name, atomic_t *val)
173{
174 struct dentry *d = __gatorfs_create_file(sb, root, name,
175 &atomic_ro_fops, 0444);
176 if (!d)
177 return -EFAULT;
178
179 d->d_inode->i_private = val;
180 return 0;
181}
182
183int gatorfs_create_file(struct super_block *sb, struct dentry *root,
184 char const *name, const struct file_operations *fops)
185{
186 if (!__gatorfs_create_file(sb, root, name, fops, 0644))
187 return -EFAULT;
188 return 0;
189}
190
191int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root,
192 char const *name, const struct file_operations *fops, int perm)
193{
194 if (!__gatorfs_create_file(sb, root, name, fops, perm))
195 return -EFAULT;
196 return 0;
197}
198
199struct dentry *gatorfs_mkdir(struct super_block *sb,
200 struct dentry *root, char const *name)
201{
202 struct dentry *dentry;
203 struct inode *inode;
204
205 dentry = d_alloc_name(root, name);
206 if (!dentry)
207 return NULL;
208 inode = gatorfs_get_inode(sb, S_IFDIR | 0755);
209 if (!inode) {
210 dput(dentry);
211 return NULL;
212 }
213 inode->i_op = &simple_dir_inode_operations;
214 inode->i_fop = &simple_dir_operations;
215 d_add(dentry, inode);
216 return dentry;
217}
218
219static int gatorfs_fill_super(struct super_block *sb, void *data, int silent)
220{
221 struct inode *root_inode;
222 struct dentry *root_dentry;
223
224 sb->s_blocksize = PAGE_CACHE_SIZE;
225 sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
226 sb->s_magic = gatorfs_MAGIC;
227 sb->s_op = &s_ops;
228 sb->s_time_gran = 1;
229
230 root_inode = gatorfs_get_inode(sb, S_IFDIR | 0755);
231 if (!root_inode)
232 return -ENOMEM;
233 root_inode->i_op = &simple_dir_inode_operations;
234 root_inode->i_fop = &simple_dir_operations;
235 root_dentry = d_alloc_root(root_inode);
236 if (!root_dentry) {
237 iput(root_inode);
238 return -ENOMEM;
239 }
240
241 sb->s_root = root_dentry;
242
243 gator_op_create_files(sb, root_dentry);
244
245 // FIXME: verify kill_litter_super removes our dentries
246 return 0;
247}
248
249static int gatorfs_get_sb(struct file_system_type *fs_type,
250 int flags, const char *dev_name, void *data, struct vfsmount *mnt)
251{
252 return get_sb_single(fs_type, flags, data, gatorfs_fill_super, mnt);
253}
254
255static struct file_system_type gatorfs_type = {
256 .owner = THIS_MODULE,
257 .name = "gatorfs",
258 .get_sb = gatorfs_get_sb,
259 .kill_sb = kill_litter_super,
260};
261
262int __init gatorfs_register(void)
263{
264 return register_filesystem(&gatorfs_type);
265}
266
267void gatorfs_unregister(void)
268{
269 unregister_filesystem(&gatorfs_type);
270}
diff --git a/gator_main.c b/gator_main.c
new file mode 100644
index 0000000..6748448
--- /dev/null
+++ b/gator_main.c
@@ -0,0 +1,990 @@
1/**
2 * Copyright (C) ARM Limited 2010. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10static unsigned long gator_protocol_version = 2;
11
12#include <linux/version.h>
13#include <linux/init.h>
14#include <linux/module.h>
15#include <linux/types.h>
16#include <linux/fs.h>
17#include <linux/sched.h>
18#include <linux/interrupt.h>
19#include <linux/irq.h>
20#include <linux/smp.h>
21#include <linux/slab.h>
22#include <linux/mm.h>
23#include <linux/dcookies.h>
24#include <linux/vmalloc.h>
25#include <asm/uaccess.h>
26
27#ifndef CONFIG_GENERIC_TRACER
28#ifndef CONFIG_TRACING
29#warning gator requires the kernel to have CONFIG_GENERIC_TRACER or CONFIG_TRACING defined
30#endif
31#endif
32
33#ifndef CONFIG_PROFILING
34#warning gator requires the kernel to have CONFIG_PROFILING defined
35#endif
36
37#ifndef CONFIG_HIGH_RES_TIMERS
38#warning gator requires the kernel to have CONFIG_HIGH_RES_TIMERS defined
39#endif
40
41#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
42#error kernels prior to 2.6.32 are not supported
43#endif
44
45#include "gator.h"
46
47/******************************************************************************
48 * DEFINES
49 ******************************************************************************/
50#define BUFFER_SIZE_DEFAULT 131072
51
52#define NO_COOKIE 0UL
53#define INVALID_COOKIE ~0UL
54
55#define PROTOCOL_FRAME ~0
56#define PROTOCOL_START_TICK 1
57#define PROTOCOL_END_TICK 3
58#define PROTOCOL_START_BACKTRACE 5
59#define PROTOCOL_END_BACKTRACE 7
60#define PROTOCOL_COOKIE 9
61#define PROTOCOL_SCHEDULER_TRACE 11
62#define PROTOCOL_COUNTERS 13
63#define PROTOCOL_ANNOTATE 15
64
65#if defined(__arm__)
66#define PC_REG regs->ARM_pc
67#else
68#define PC_REG regs->ip
69#endif
70
71/******************************************************************************
72 * PER CPU
73 ******************************************************************************/
74static unsigned long gator_buffer_size;
75static unsigned long gator_backtrace_depth;
76
77static unsigned long gator_started;
78static unsigned long gator_buffer_opened;
79static unsigned long gator_timer_count;
80static unsigned long gator_dump;
81static DEFINE_MUTEX(start_mutex);
82static DEFINE_MUTEX(gator_buffer_mutex);
83static DECLARE_WAIT_QUEUE_HEAD(gator_buffer_wait);
84/* atomic_t because wait_event checks it outside of gator_buffer_mutex */
85static DEFINE_PER_CPU(atomic_t, gator_buffer_ready);
86static DEFINE_PER_CPU(int, gator_tick);
87static DEFINE_PER_CPU(int, gator_first_time);
88
89/******************************************************************************
90 * Prototypes
91 ******************************************************************************/
92static void gator_buffer_write_packed_int(int cpu, unsigned int x);
93static void gator_buffer_write_string(int cpu, char *x);
94static void gator_add_trace(int cpu, unsigned int address);
95static uint64_t gator_get_time(void);
96
97/******************************************************************************
98 * Application Includes
99 ******************************************************************************/
100#include "gator_cookies.c"
101#include "gator_trace_sched.c"
102#include "gator_backtrace.c"
103#include "gator_annotate.c"
104#include "gator_events.c"
105#include "gator_fs.c"
106
107/******************************************************************************
108 * Misc
109 ******************************************************************************/
110#if defined(__arm__)
111u32 gator_cpuid(void)
112{
113 u32 val;
114 asm volatile("mrc p15, 0, %0, c0, c0, 0" : "=r" (val));
115 return (val >> 4) & 0xfff;
116}
117#endif
118/******************************************************************************
119 * Buffer management
120 ******************************************************************************/
121static uint32_t use_buffer_size;
122static uint32_t use_buffer_mask;
123static DEFINE_PER_CPU(int, use_buffer_seq);
124static DEFINE_PER_CPU(int, use_buffer_read);
125static DEFINE_PER_CPU(int, use_buffer_write);
126static DEFINE_PER_CPU(int, use_buffer_commit);
127static DEFINE_PER_CPU(char *, use_buffer);
128
129static void gator_buffer_write_packed_int(int cpu, unsigned int x)
130{
131 uint32_t write = per_cpu(use_buffer_write, cpu);
132 uint32_t mask = use_buffer_mask;
133 char *buffer = per_cpu(use_buffer, cpu);
134 int write0 = (write + 0) & mask;
135 int write1 = (write + 1) & mask;
136 int write2 = (write + 2) & mask;
137 int write3 = (write + 3) & mask;
138 int write4 = (write + 4) & mask;
139 int write5 = (write + 5) & mask;
140
141 if ((x & 0xffffff80) == 0) {
142 buffer[write0] = x & 0x7f;
143 per_cpu(use_buffer_write, cpu) = write1;
144 } else if ((x & 0xffffc000) == 0) {
145 buffer[write0] = x | 0x80;
146 buffer[write1] = (x>>7) & 0x7f;
147 per_cpu(use_buffer_write, cpu) = write2;
148 } else if ((x & 0xffe00000) == 0) {
149 buffer[write0] = x | 0x80;
150 buffer[write1] = (x>>7) | 0x80;
151 buffer[write2] = (x>>14) & 0x7f;
152 per_cpu(use_buffer_write, cpu) = write3;
153 } else if ((x & 0xf0000000) == 0) {
154 buffer[write0] = x | 0x80;
155 buffer[write1] = (x>>7) | 0x80;
156 buffer[write2] = (x>>14) | 0x80;
157 buffer[write3] = (x>>21) & 0x7f;
158 per_cpu(use_buffer_write, cpu) = write4;
159 } else {
160 buffer[write0] = x | 0x80;
161 buffer[write1] = (x>>7) | 0x80;
162 buffer[write2] = (x>>14) | 0x80;
163 buffer[write3] = (x>>21) | 0x80;
164 buffer[write4] = (x>>28) & 0x0f;
165 per_cpu(use_buffer_write, cpu) = write5;
166 }
167}
168
169static void gator_buffer_write_bytes(int cpu, char *x, int len)
170{
171 uint32_t write = per_cpu(use_buffer_write, cpu);
172 uint32_t mask = use_buffer_mask;
173 char *buffer = per_cpu(use_buffer, cpu);
174 int i;
175
176 for (i = 0; i < len; i++) {
177 buffer[write] = x[i];
178 write = (write + 1) & mask;
179 }
180
181 per_cpu(use_buffer_write, cpu) = write;
182}
183
184static void gator_buffer_write_string(int cpu, char *x)
185{
186 int len = strlen(x);
187 gator_buffer_write_packed_int(cpu, len);
188 gator_buffer_write_bytes(cpu, x, len);
189}
190
191static void gator_buffer_header(int cpu)
192{
193 gator_buffer_write_packed_int(cpu, PROTOCOL_FRAME);
194 gator_buffer_write_packed_int(cpu, cpu);
195 gator_buffer_write_packed_int(cpu, per_cpu(use_buffer_seq, cpu));
196}
197
198static void gator_buffer_commit(int cpu)
199{
200 per_cpu(use_buffer_commit, cpu) = per_cpu(use_buffer_write, cpu);
201
202 per_cpu(use_buffer_seq, cpu)++;
203 gator_buffer_header(cpu);
204
205 atomic_set(&per_cpu(gator_buffer_ready, cpu), 1);
206 wake_up(&gator_buffer_wait);
207}
208
209static void gator_buffer_check(int cpu)
210{
211 int commit = 0;
212 int available = per_cpu(use_buffer_write, cpu) - per_cpu(use_buffer_read, cpu);
213 if (available < 0) {
214 available += use_buffer_size;
215 }
216
217 if (!cpu && gator_dump) {
218 gator_dump = 0;
219 commit = 1;
220 } else if (available >= ((use_buffer_size * 3) / 4)) {
221 commit = 1;
222 } else if (cpu && (per_cpu(use_buffer_seq, cpu) < per_cpu(use_buffer_seq, 0))) {
223 commit = 1;
224 }
225
226 if (commit) {
227 gator_buffer_commit(cpu);
228 }
229}
230
231static void gator_add_trace(int cpu, unsigned int address)
232{
233 off_t offset = 0;
234 unsigned long cookie = get_address_cookie(cpu, current, address & ~1, &offset);
235
236 if (cookie == NO_COOKIE || cookie == INVALID_COOKIE) {
237 offset = address;
238 }
239
240 gator_buffer_write_packed_int(cpu, offset & ~1);
241 gator_buffer_write_packed_int(cpu, cookie);
242}
243
244static void gator_add_sample(int cpu, struct pt_regs * const regs)
245{
246 int inKernel = regs ? !user_mode(regs) : 1;
247 unsigned long cookie = !inKernel ? get_exec_cookie(cpu, current) : NO_COOKIE;
248
249 gator_buffer_write_packed_int(cpu, PROTOCOL_START_BACKTRACE);
250
251 // TGID::PID::inKernel
252 gator_buffer_write_packed_int(cpu, cookie);
253 gator_buffer_write_packed_int(cpu, (unsigned int)current->tgid);
254 gator_buffer_write_packed_int(cpu, (unsigned int)current->pid);
255 gator_buffer_write_packed_int(cpu, inKernel);
256
257 // get_irq_regs() will return NULL outside of IRQ context (e.g. nested IRQ)
258 if (regs) {
259 if (inKernel) {
260 gator_buffer_write_packed_int(cpu, PC_REG & ~1);
261 gator_buffer_write_packed_int(cpu, 0); // cookie
262 } else {
263 // Cookie+PC
264 gator_add_trace(cpu, PC_REG);
265
266 // Backtrace
267 if (gator_backtrace_depth)
268 arm_backtrace_eabi(cpu, regs, gator_backtrace_depth);
269 }
270 }
271
272 gator_buffer_write_packed_int(cpu, PROTOCOL_END_BACKTRACE);
273}
274
275static void gator_write_packet(int cpu, int type, int len, int *buffer)
276{
277 int i;
278 gator_buffer_write_packed_int(cpu, type);
279 gator_buffer_write_packed_int(cpu, len);
280 for (i = 0; i < len; i++) {
281 gator_buffer_write_packed_int(cpu, buffer[i]);
282 }
283}
284
285static void gator_write_annotate(int cpu, int len, int *buffer)
286{
287 int pos = 0;
288
289 while (pos < len) {
290 unsigned int tid = buffer[pos++];
291 unsigned int bytes = buffer[pos++];
292 unsigned int words = (bytes + 3) / 4;
293 char *ptr = (char *)&buffer[pos];
294 pos += words;
295
296 gator_buffer_write_packed_int(cpu, PROTOCOL_ANNOTATE);
297 gator_buffer_write_packed_int(cpu, tid);
298 gator_buffer_write_packed_int(cpu, bytes);
299 gator_buffer_write_bytes(cpu, ptr, bytes);
300 }
301}
302
303/******************************************************************************
304 * Interrupt Processing
305 ******************************************************************************/
306static gator_interface *gi = NULL;
307
308static void gator_timer_interrupt(unsigned int ticks)
309{
310 struct pt_regs * const regs = get_irq_regs();
311 int cpu = raw_smp_processor_id();
312 int *buffer, len;
313 gator_interface *i;
314
315 // check full backtrace has enough space, otherwise may
316 // have breaks between samples in the same callstack
317 if (per_cpu(gator_first_time, cpu)) {
318 per_cpu(gator_first_time, cpu) = 0;
319
320 for (i = gi; i != NULL; i = i->next) {
321 if (i->read) {
322 i->read(NULL);
323 }
324 }
325 per_cpu(gator_tick, cpu) += ticks;
326 return;
327 }
328
329 // Header
330 gator_buffer_write_packed_int(cpu, PROTOCOL_START_TICK); // Escape
331
332 // Output scheduler
333 len = gator_trace_sched_read(&buffer);
334 if (len > 0) {
335 gator_write_packet(cpu, PROTOCOL_SCHEDULER_TRACE, len, buffer);
336 }
337
338 // Output annotate
339 len = gator_annotate_read(&buffer);
340 if (len > 0)
341 gator_write_annotate(cpu, len, buffer);
342
343 // Output counters
344 for (i = gi; i != NULL; i = i->next) {
345 if (i->read) {
346 len = i->read(&buffer);
347 if (len > 0) {
348 gator_write_packet(cpu, PROTOCOL_COUNTERS, len, buffer);
349 }
350 }
351 }
352
353 // Output backtrace
354 gator_add_sample(cpu, regs);
355
356 // Timer Tick
357 gator_write_packet(cpu, PROTOCOL_END_TICK, 1, &per_cpu(gator_tick, cpu));
358 per_cpu(gator_tick, cpu) += ticks;
359
360 // Check and commit
361 gator_buffer_check(cpu);
362}
363
364/******************************************************************************
365 * hrtimer
366 ******************************************************************************/
367DEFINE_PER_CPU(struct hrtimer, percpu_hrtimer);
368extern void gator_timer_stop(void);
369static int hrtimer_running;
370static ktime_t profiling_interval;
371static atomic_t num_active_timers;
372
373static enum hrtimer_restart gator_hrtimer_notify(struct hrtimer *hrtimer)
374{
375 unsigned int ticks = (unsigned int)hrtimer_forward_now(hrtimer, profiling_interval);
376 gator_timer_interrupt(ticks);
377 return HRTIMER_RESTART;
378}
379
380int gator_timer_init(void)
381{
382 return 0;
383}
384
385static void __gator_timer_start(void *unused)
386{
387 int cpu = smp_processor_id();
388
389 struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu);
390 hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
391 hrtimer->function = gator_hrtimer_notify;
392 hrtimer_start(hrtimer, profiling_interval, HRTIMER_MODE_REL_PINNED);
393 atomic_inc(&num_active_timers);
394
395 per_cpu(gator_first_time, cpu) = 1;
396 per_cpu(gator_tick, cpu) = 0;
397}
398
399int gator_timer_start(unsigned long setup)
400{
401 atomic_set(&num_active_timers, 0);
402
403 if (!setup) {
404 pr_err("gator: cannot start due to a system tick value of zero");
405 return -1;
406 } else if (hrtimer_running) {
407 pr_notice("gator: high res timer already running");
408 return 0;
409 }
410
411 hrtimer_running = 1;
412
413 // calculate profiling interval
414 profiling_interval = ns_to_ktime(1000000000UL / setup);
415
416 // timer interrupt
417 on_each_cpu(__gator_timer_start, NULL, 1);
418
419 if (atomic_read(&num_active_timers) != num_present_cpus()) {
420 pr_err("gator: %d timer(s) started yet %d cpu(s) detected, please bring all cpus online manually and restart\n", atomic_read(&num_active_timers), nr_cpu_ids);
421 gator_timer_stop();
422 return -1;
423 }
424
425 return 0;
426}
427
428void __gator_timer_stop(void *unused)
429{
430 struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, smp_processor_id());
431 hrtimer_cancel(hrtimer);
432 atomic_dec(&num_active_timers);
433}
434
435void gator_timer_stop(void)
436{
437 if (hrtimer_running) {
438 int previously_running = atomic_read(&num_active_timers);
439 hrtimer_running = 0;
440 on_each_cpu(__gator_timer_stop, NULL, 1);
441 if (atomic_read(&num_active_timers) != 0) {
442 pr_err("gator: %d timers stopped yet %d were running, it is advised to quit and restart gatord", previously_running - atomic_read(&num_active_timers), previously_running);
443 atomic_set(&num_active_timers, 0);
444 }
445 }
446}
447
448static uint64_t gator_get_time(void)
449{
450 struct timespec ts;
451 uint64_t timestamp;
452
453 ktime_get_ts(&ts);
454 timestamp = timespec_to_ns(&ts);
455
456 return timestamp;
457}
458
459/******************************************************************************
460 * Main
461 ******************************************************************************/
462int gator_event_install(int (*event_install)(gator_interface *))
463{
464 gator_interface *ni = (gator_interface*)kmalloc(sizeof(gator_interface), GFP_KERNEL);
465 if (ni == NULL) {
466 return -1;
467 }
468
469 ni->create_files = NULL;
470 ni->init = NULL;
471 ni->start = NULL;
472 ni->stop = NULL;
473 ni->read = NULL;
474 ni->next = NULL;
475
476 // Initialize ni gator interface
477 if (!event_install(ni)) {
478 if (gi == NULL) {
479 // Set gi to point to the first gator interface
480 gi = ni;
481 } else {
482 // Link the gator interfaces
483 gator_interface *i = gi;
484 while (i->next) {
485 i = i->next;
486 }
487 i->next = ni;
488 }
489 } else {
490 kfree(ni);
491 }
492
493 return 0;
494}
495
496static int gator_init(void)
497{
498 gator_interface *i;
499 int key = 0;
500
501 if (gator_timer_init())
502 return -1;
503 if (gator_trace_sched_init())
504 return -1;
505 if (gator_annotate_init())
506 return -1;
507
508 // set up gator interface linked list structure
509 if (gator_events_install())
510 return -1;
511
512 // initialize all events
513 for (i = gi; i != NULL; i = i->next) {
514 if (i->init) {
515 if (i->init(&key)) {
516 return -1;
517 }
518 }
519 }
520
521 return 0;
522}
523
524static int gator_start(void)
525{
526 gator_interface *i, *f;
527
528 // start all events
529 for (i = gi; i != NULL; i = i->next) {
530 if (i->start) {
531 if (i->start()) {
532 goto events_failure;
533 }
534 }
535 }
536
537 if (gator_annotate_start())
538 goto annotate_failure;
539 if (gator_trace_sched_start())
540 goto sched_failure;
541 if (gator_timer_start(gator_timer_count))
542 goto timer_failure;
543
544 return 0;
545
546timer_failure:
547 gator_trace_sched_stop();
548sched_failure:
549 gator_annotate_stop();
550annotate_failure:
551events_failure:
552 for (f = gi; f != i; f = f->next) {
553 f->stop();
554 }
555
556 return -1;
557}
558
559static void gator_stop(void)
560{
561 gator_interface *i;
562
563 // stop all interrupt callback reads before tearing down other interfaces
564 gator_timer_stop();
565 gator_annotate_stop();
566 gator_trace_sched_stop();
567
568 // stop all events
569 for (i = gi; i != NULL; i = i->next) {
570 if (i->stop) {
571 i->stop();
572 }
573 }
574}
575
576static void gator_exit(void)
577{
578 gator_interface *i = gi;
579
580 while (i) {
581 gator_interface *p = i;
582 i = i->next;
583 kfree(p);
584 }
585}
586
587/******************************************************************************
588 * Filesystem
589 ******************************************************************************/
590/* fopen("buffer") */
591static int gator_op_setup(void)
592{
593 int err = 0;
594 int cpu;
595
596 mutex_lock(&start_mutex);
597
598 use_buffer_size = gator_buffer_size;
599 use_buffer_mask = use_buffer_size - 1;
600
601 // must be a power of 2
602 if (use_buffer_size & (use_buffer_size - 1)) {
603 err = -ENOEXEC;
604 goto setup_error;
605 }
606
607 for_each_possible_cpu(cpu) {
608 per_cpu(use_buffer, cpu) = vmalloc(use_buffer_size);
609 if (!per_cpu(use_buffer, cpu)) {
610 err = -ENOMEM;
611 goto setup_error;
612 }
613
614 atomic_set(&per_cpu(gator_buffer_ready, cpu), 0);
615 per_cpu(use_buffer_seq, cpu) = 0;
616 per_cpu(use_buffer_read, cpu) = 0;
617 per_cpu(use_buffer_write, cpu) = 0;
618 per_cpu(use_buffer_commit, cpu) = 0;
619 gator_buffer_header(cpu);
620 }
621
622setup_error:
623 mutex_unlock(&start_mutex);
624 return err;
625}
626
627/* Actually start profiling (echo 1>/dev/gator/enable) */
628static int gator_op_start(void)
629{
630 int err = 0;
631
632 mutex_lock(&start_mutex);
633
634 if (gator_started || gator_start())
635 err = -EINVAL;
636 else
637 gator_started = 1;
638
639 cookies_initialize();
640
641 mutex_unlock(&start_mutex);
642
643 return err;
644}
645
646/* echo 0>/dev/gator/enable */
647static void gator_op_stop(void)
648{
649 int cpu;
650
651 mutex_lock(&start_mutex);
652
653 if (gator_started) {
654 gator_stop();
655
656 mutex_lock(&gator_buffer_mutex);
657
658 /* wake up the daemon to read what remains */
659 for_each_possible_cpu(cpu) {
660 gator_buffer_commit(cpu);
661 atomic_set(&per_cpu(gator_buffer_ready, cpu), 1);
662 }
663 gator_started = 0;
664 cookies_release();
665 wake_up(&gator_buffer_wait);
666
667 mutex_unlock(&gator_buffer_mutex);
668 }
669
670 mutex_unlock(&start_mutex);
671}
672
673static void gator_shutdown(void)
674{
675 int cpu;
676
677 mutex_lock(&start_mutex);
678
679 for_each_possible_cpu(cpu) {
680 mutex_lock(&gator_buffer_mutex);
681 vfree(per_cpu(use_buffer, cpu));
682 per_cpu(use_buffer, cpu) = NULL;
683 per_cpu(use_buffer_seq, cpu) = 0;
684 per_cpu(use_buffer_read, cpu) = 0;
685 per_cpu(use_buffer_write, cpu) = 0;
686 per_cpu(use_buffer_commit, cpu) = 0;
687 mutex_unlock(&gator_buffer_mutex);
688 }
689
690 mutex_unlock(&start_mutex);
691}
692
693static int gator_set_backtrace(unsigned long val)
694{
695 int err = 0;
696
697 mutex_lock(&start_mutex);
698
699 if (gator_started)
700 err = -EBUSY;
701 else
702 gator_backtrace_depth = val;
703
704 mutex_unlock(&start_mutex);
705
706 return err;
707}
708
709static ssize_t enable_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
710{
711 return gatorfs_ulong_to_user(gator_started, buf, count, offset);
712}
713
714static ssize_t enable_write(struct file *file, char const __user *buf, size_t count, loff_t *offset)
715{
716 unsigned long val;
717 int retval;
718
719 if (*offset)
720 return -EINVAL;
721
722 retval = gatorfs_ulong_from_user(&val, buf, count);
723 if (retval)
724 return retval;
725
726 if (val)
727 retval = gator_op_start();
728 else
729 gator_op_stop();
730
731 if (retval)
732 return retval;
733 return count;
734}
735
736static const struct file_operations enable_fops = {
737 .read = enable_read,
738 .write = enable_write,
739};
740
741static int event_buffer_open(struct inode *inode, struct file *file)
742{
743 int err = -EPERM;
744
745 if (!capable(CAP_SYS_ADMIN))
746 return -EPERM;
747
748 if (test_and_set_bit_lock(0, &gator_buffer_opened))
749 return -EBUSY;
750
751 /* Register as a user of dcookies
752 * to ensure they persist for the lifetime of
753 * the open event file
754 */
755 err = -EINVAL;
756 file->private_data = dcookie_register();
757 if (!file->private_data)
758 goto out;
759
760 if ((err = gator_op_setup()))
761 goto fail;
762
763 /* NB: the actual start happens from userspace
764 * echo 1 >/dev/gator/enable
765 */
766
767 return 0;
768
769fail:
770 dcookie_unregister(file->private_data);
771out:
772 __clear_bit_unlock(0, &gator_buffer_opened);
773 return err;
774}
775
776static int event_buffer_release(struct inode *inode, struct file *file)
777{
778 int cpu;
779 gator_op_stop();
780 gator_shutdown();
781 dcookie_unregister(file->private_data);
782 for_each_possible_cpu(cpu) {
783 atomic_set(&per_cpu(gator_buffer_ready, cpu), 0);
784 }
785 __clear_bit_unlock(0, &gator_buffer_opened);
786 return 0;
787}
788
789static int event_buffer_ready(void)
790{
791 int cpu;
792 for_each_possible_cpu(cpu) {
793 if (atomic_read(&per_cpu(gator_buffer_ready, cpu)))
794 return cpu;
795 }
796 return -1;
797}
798
799static ssize_t event_buffer_read(struct file *file, char __user *buf,
800 size_t count, loff_t *offset)
801{
802 int retval = -EINVAL;
803 int commit, length1, length2, read;
804 char *buffer1, *buffer2;
805 int cpu;
806
807 /* do not handle partial reads */
808 if (count != use_buffer_size || *offset)
809 return -EINVAL;
810
811 wait_event_interruptible(gator_buffer_wait, event_buffer_ready() >= 0 || !gator_started);
812 cpu = event_buffer_ready();
813
814 if (signal_pending(current))
815 return -EINTR;
816
817 if (cpu < 0)
818 return 0;
819
820 /* should not happen */
821 if (!atomic_read(&per_cpu(gator_buffer_ready, cpu)))
822 return -EAGAIN;
823
824 mutex_lock(&gator_buffer_mutex);
825
826 retval = -EFAULT;
827
828 /* May happen if the buffer is freed during pending reads. */
829 if (!per_cpu(use_buffer, cpu)) {
830 retval = -EFAULT;
831 goto out;
832 }
833
834 /* determine the size of two halves */
835 commit = per_cpu(use_buffer_commit, cpu);
836 read = per_cpu(use_buffer_read, cpu);
837 length1 = commit - read;
838 length2 = 0;
839 buffer1 = &(per_cpu(use_buffer, cpu)[read]);
840 buffer2 = &(per_cpu(use_buffer, cpu)[0]);
841 if (length1 < 0) {
842 length1 = use_buffer_size - read;
843 length2 = commit;
844 }
845
846 /* start, middle or end */
847 if (length1 > 0) {
848 if (copy_to_user(&buf[0], buffer1, length1)) {
849 goto out;
850 }
851 }
852
853 /* possible wrap around */
854 if (length2 > 0) {
855 if (copy_to_user(&buf[length1], buffer2, length2)) {
856 goto out;
857 }
858 }
859
860 /* update position */
861 retval = length1 + length2;
862 per_cpu(use_buffer_read, cpu) = (read + retval) & use_buffer_mask;
863
864 atomic_set(&per_cpu(gator_buffer_ready, cpu), 0);
865
866 /* kick just in case we've lost an SMP event */
867 wake_up(&gator_buffer_wait);
868
869out:
870 mutex_unlock(&gator_buffer_mutex);
871 return retval;
872}
873
874const struct file_operations gator_event_buffer_fops = {
875 .open = event_buffer_open,
876 .release = event_buffer_release,
877 .read = event_buffer_read,
878};
879
880static ssize_t depth_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
881{
882 return gatorfs_ulong_to_user(gator_backtrace_depth, buf, count,
883 offset);
884}
885
886static ssize_t depth_write(struct file *file, char const __user *buf, size_t count, loff_t *offset)
887{
888 unsigned long val;
889 int retval;
890
891 if (*offset)
892 return -EINVAL;
893
894 retval = gatorfs_ulong_from_user(&val, buf, count);
895 if (retval)
896 return retval;
897
898 retval = gator_set_backtrace(val);
899
900 if (retval)
901 return retval;
902 return count;
903}
904
905static const struct file_operations depth_fops = {
906 .read = depth_read,
907 .write = depth_write
908};
909
910static const char gator_cpu_type[] = "gator";
911
912static ssize_t cpu_type_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
913{
914 return gatorfs_str_to_user(gator_cpu_type, buf, count, offset);
915}
916
917static const struct file_operations cpu_type_fops = {
918 .read = cpu_type_read,
919};
920
921void gator_op_create_files(struct super_block *sb, struct dentry *root)
922{
923 struct dentry *dir;
924 gator_interface *i;
925
926 /* reinitialize default values */
927 gator_buffer_size = BUFFER_SIZE_DEFAULT;
928
929 gatorfs_create_file(sb, root, "enable", &enable_fops);
930 gatorfs_create_file(sb, root, "buffer", &gator_event_buffer_fops);
931 gatorfs_create_file(sb, root, "backtrace_depth", &depth_fops);
932 gatorfs_create_file(sb, root, "cpu_type", &cpu_type_fops);
933 gatorfs_create_ulong(sb, root, "buffer_size", &gator_buffer_size);
934 gatorfs_create_ulong(sb, root, "tick", &gator_timer_count);
935 gatorfs_create_ulong(sb, root, "dump", &gator_dump);
936 gatorfs_create_ro_ulong(sb, root, "version", &gator_protocol_version);
937
938 // Annotate interface
939 gator_annotate_create_files(sb, root);
940
941 // Linux Events
942 dir = gatorfs_mkdir(sb, root, "events");
943 for (i = gi; i != NULL; i = i->next) {
944 if (i->create_files) {
945 i->create_files(sb, dir);
946 }
947 }
948}
949
950/******************************************************************************
951 * Module
952 ******************************************************************************/
953static int gator_initialized;
954
955static int __init gator_module_init(void)
956{
957 if (gatorfs_register()) {
958 return -1;
959 }
960
961 if (gator_init()) {
962 gatorfs_unregister();
963 return -1;
964 }
965
966 gator_initialized = 1;
967#ifdef GATOR_DEBUG
968 pr_err("gator_module_init");
969#endif
970 return 0;
971}
972
973static void __exit gator_module_exit(void)
974{
975#ifdef GATOR_DEBUG
976 pr_err("gator_module_exit");
977#endif
978 gatorfs_unregister();
979 if (gator_initialized) {
980 gator_initialized = 0;
981 gator_exit();
982 }
983}
984
985module_init(gator_module_init);
986module_exit(gator_module_exit);
987
988MODULE_LICENSE("GPL");
989MODULE_AUTHOR("ARM Ltd");
990MODULE_DESCRIPTION("Gator system profiler");
diff --git a/gator_trace.h b/gator_trace.h
new file mode 100644
index 0000000..446d37b
--- /dev/null
+++ b/gator_trace.h
@@ -0,0 +1,26 @@
1/**
2 * Copyright 2010 ARM, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
11# error Kernels prior to 2.6.32 not supported
12#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
13# define GATOR_DEFINE_PROBE(probe_name, proto) \
14 static void probe_##probe_name(PARAMS(proto))
15# define GATOR_REGISTER_TRACE(probe_name) \
16 register_trace_##probe_name(probe_##probe_name)
17# define GATOR_UNREGISTER_TRACE(probe_name) \
18 unregister_trace_##probe_name(probe_##probe_name)
19#else
20# define GATOR_DEFINE_PROBE(probe_name, proto) \
21 static void probe_##probe_name(void *data, PARAMS(proto))
22# define GATOR_REGISTER_TRACE(probe_name) \
23 register_trace_##probe_name(probe_##probe_name, NULL)
24# define GATOR_UNREGISTER_TRACE(probe_name) \
25 unregister_trace_##probe_name(probe_##probe_name, NULL)
26#endif
diff --git a/gator_trace_sched.c b/gator_trace_sched.c
new file mode 100644
index 0000000..1328d88
--- /dev/null
+++ b/gator_trace_sched.c
@@ -0,0 +1,269 @@
1/**
2 * Copyright (C) ARM Limited 2010. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 */
9
10#include <linux/slab.h>
11#include <linux/fs.h>
12#include <linux/mm.h>
13#include <linux/version.h>
14#include <linux/hardirq.h>
15#include <trace/events/sched.h>
16
17#include "gator_trace.h"
18
19#define SCHED_TIMER_EVENT 0
20#define SCHED_WAIT_TASK 1
21#define SCHED_WAKEUP 2
22#define SCHED_WAKEUP_NEW 3
23#define SCHED_SWITCH 4
24#define SCHED_MIGRATE_TASK 5
25#define SCHED_PROCESS_FREE 6
26#define SCHED_PROCESS_EXIT 7
27#define SCHED_PROCESS_WAIT 8
28#define SCHED_PROCESS_FORK 9
29#define SCHED_OVERFLOW -1
30
31#define SCHEDSIZE (16*1024)
32
33static DEFINE_PER_CPU(int *[2], theSchedBuf);
34static DEFINE_PER_CPU(int, theSchedSel);
35static DEFINE_PER_CPU(int, theSchedPos);
36static DEFINE_PER_CPU(int, theSchedErr);
37
38static void probe_sched_write(int type, int param1, int param2, int param3)
39{
40 unsigned long flags;
41 int cpu = smp_processor_id();
42 uint64_t time = gator_get_time();
43 int *schedBuf;
44 int schedPos;
45
46 if (!per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)])
47 return;
48
49 // disable interrupts to synchronize with gator_trace_sched_read(); spinlocks not needed since percpu buffers are used
50 local_irq_save(flags);
51
52 schedPos = per_cpu(theSchedPos, cpu);
53 schedBuf = per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)];
54
55 if (schedPos < (SCHEDSIZE-100)) {
56 // capture
57 schedBuf[schedPos+0] = type;
58 schedBuf[schedPos+1] = (int)time;
59 schedBuf[schedPos+2] = (int)(time >> 32);
60 schedBuf[schedPos+3] = param1;
61 schedBuf[schedPos+4] = param2;
62 schedBuf[schedPos+5] = param3;
63 per_cpu(theSchedPos, cpu) = schedPos + 6;
64 } else if (!per_cpu(theSchedErr, cpu)) {
65 per_cpu(theSchedErr, cpu) = 1;
66 schedBuf[schedPos+0] = SCHED_OVERFLOW;
67 schedBuf[schedPos+1] = 0;
68 schedBuf[schedPos+2] = 0;
69 schedBuf[schedPos+3] = 0;
70 schedBuf[schedPos+4] = 0;
71 schedBuf[schedPos+5] = 0;
72 per_cpu(theSchedPos, cpu) = schedPos + 6;
73 pr_debug("gator: tracepoint overflow\n");
74 }
75 local_irq_restore(flags);
76}
77
78#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
79GATOR_DEFINE_PROBE(sched_wait_task, TP_PROTO(struct rq *rq, struct task_struct *p))
80#else
81GATOR_DEFINE_PROBE(sched_wait_task, TP_PROTO(struct task_struct *p))
82#endif
83{
84 probe_sched_write(SCHED_WAIT_TASK, 0, p->pid, 0);
85}
86
87#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
88GATOR_DEFINE_PROBE(sched_wakeup, TP_PROTO(struct rq *rq, struct task_struct *p, int success))
89#else
90GATOR_DEFINE_PROBE(sched_wakeup, TP_PROTO(struct task_struct *p, int success))
91#endif
92{
93 if (success)
94 probe_sched_write(SCHED_WAKEUP, 0, p->pid, 0);
95}
96
97#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
98GATOR_DEFINE_PROBE(sched_wakeup_new, TP_PROTO(struct rq *rq, struct task_struct *p, int success))
99#else
100GATOR_DEFINE_PROBE(sched_wakeup_new, TP_PROTO(struct task_struct *p, int success))
101#endif
102{
103 if (success)
104 probe_sched_write(SCHED_WAKEUP_NEW, 0, p->tgid, p->pid);
105}
106
107#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
108GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next))
109#else
110GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next))
111#endif
112{
113 probe_sched_write(SCHED_SWITCH, (int)next, next->tgid, next->pid);
114}
115
116GATOR_DEFINE_PROBE(sched_migrate_task, TP_PROTO(struct task_struct *p, int dest_cpu))
117{
118 probe_sched_write(SCHED_MIGRATE_TASK, 0, dest_cpu, p->pid);
119}
120
121GATOR_DEFINE_PROBE(sched_process_free, TP_PROTO(struct task_struct *p))
122{
123 probe_sched_write(SCHED_PROCESS_FREE, 0, p->pid, 0);
124}
125
126GATOR_DEFINE_PROBE(sched_process_exit, TP_PROTO(struct task_struct *p))
127{
128 probe_sched_write(SCHED_PROCESS_EXIT, 0, p->pid, 0);
129}
130
131GATOR_DEFINE_PROBE(sched_process_wait, TP_PROTO(struct pid *pid))
132{
133 probe_sched_write(SCHED_PROCESS_WAIT, 0, pid_nr(pid), 0);
134}
135
136GATOR_DEFINE_PROBE(sched_process_fork, TP_PROTO(struct task_struct *parent, struct task_struct *child))
137{
138 probe_sched_write(SCHED_PROCESS_FORK, (int)child, parent->pid, child->pid);
139}
140
141int gator_trace_sched_init(void)
142{
143 return 0;
144}
145
146int gator_trace_sched_start(void)
147{
148 int cpu;
149
150 for_each_possible_cpu(cpu) {
151 per_cpu(theSchedSel, cpu) = 0;
152 per_cpu(theSchedPos, cpu) = 0;
153 per_cpu(theSchedErr, cpu) = 0;
154 per_cpu(theSchedBuf, cpu)[0] = kmalloc(SCHEDSIZE * sizeof(int), GFP_KERNEL);
155 per_cpu(theSchedBuf, cpu)[1] = kmalloc(SCHEDSIZE * sizeof(int), GFP_KERNEL);
156 if (!per_cpu(theSchedBuf, cpu))
157 return -1;
158 }
159
160 // register tracepoints
161 if (GATOR_REGISTER_TRACE(sched_wait_task))
162 goto fail_sched_wait_task;
163 if (GATOR_REGISTER_TRACE(sched_wakeup))
164 goto fail_sched_wakeup;
165 if (GATOR_REGISTER_TRACE(sched_wakeup_new))
166 goto fail_sched_wakeup_new;
167 if (GATOR_REGISTER_TRACE(sched_switch))
168 goto fail_sched_switch;
169 if (GATOR_REGISTER_TRACE(sched_migrate_task))
170 goto fail_sched_migrate_task;
171 if (GATOR_REGISTER_TRACE(sched_process_free))
172 goto fail_sched_process_free;
173 if (GATOR_REGISTER_TRACE(sched_process_exit))
174 goto fail_sched_process_exit;
175 if (GATOR_REGISTER_TRACE(sched_process_wait))
176 goto fail_sched_process_wait;
177 if (GATOR_REGISTER_TRACE(sched_process_fork))
178 goto fail_sched_process_fork;
179 pr_debug("gator: registered tracepoints\n");
180
181 return 0;
182
183 // unregister tracepoints on error
184fail_sched_process_fork:
185 GATOR_UNREGISTER_TRACE(sched_process_wait);
186fail_sched_process_wait:
187 GATOR_UNREGISTER_TRACE(sched_process_exit);
188fail_sched_process_exit:
189 GATOR_UNREGISTER_TRACE(sched_process_free);
190fail_sched_process_free:
191 GATOR_UNREGISTER_TRACE(sched_migrate_task);
192fail_sched_migrate_task:
193 GATOR_UNREGISTER_TRACE(sched_switch);
194fail_sched_switch:
195 GATOR_UNREGISTER_TRACE(sched_wakeup_new);
196fail_sched_wakeup_new:
197 GATOR_UNREGISTER_TRACE(sched_wakeup);
198fail_sched_wakeup:
199 GATOR_UNREGISTER_TRACE(sched_wait_task);
200fail_sched_wait_task:
201 pr_err("gator: tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
202
203 return -1;
204}
205
206void gator_trace_sched_stop(void)
207{
208 int cpu;
209 GATOR_UNREGISTER_TRACE(sched_wait_task);
210 GATOR_UNREGISTER_TRACE(sched_wakeup);
211 GATOR_UNREGISTER_TRACE(sched_wakeup_new);
212 GATOR_UNREGISTER_TRACE(sched_switch);
213 GATOR_UNREGISTER_TRACE(sched_migrate_task);
214 GATOR_UNREGISTER_TRACE(sched_process_free);
215 GATOR_UNREGISTER_TRACE(sched_process_exit);
216 GATOR_UNREGISTER_TRACE(sched_process_wait);
217 GATOR_UNREGISTER_TRACE(sched_process_fork);
218 pr_debug("gator: unregistered tracepoints\n");
219
220 for_each_possible_cpu(cpu) {
221 kfree(per_cpu(theSchedBuf, cpu)[0]);
222 kfree(per_cpu(theSchedBuf, cpu)[1]);
223 per_cpu(theSchedBuf, cpu)[0] = NULL;
224 per_cpu(theSchedBuf, cpu)[1] = NULL;
225 }
226}
227
228int gator_trace_sched_read(int **buffer)
229{
230 uint64_t time = gator_get_time();
231 int cpu = smp_processor_id();
232 unsigned long flags;
233 int *schedBuf;
234 int schedPos;
235 int i;
236
237 if (!per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)])
238 return 0;
239
240 local_irq_save(flags);
241
242 schedBuf = per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)];
243 schedPos = per_cpu(theSchedPos, cpu);
244
245 per_cpu(theSchedSel, cpu) = !per_cpu(theSchedSel, cpu);
246 per_cpu(theSchedPos, cpu) = 0;
247 per_cpu(theSchedErr, cpu) = 0;
248
249 local_irq_restore(flags);
250
251 // find mm and replace with cookies
252 for (i = 0; i < schedPos; i += 6) {
253 uint32_t cookie = schedBuf[i+3];
254 if (cookie) {
255 struct task_struct *task = (struct task_struct *)cookie;
256 schedBuf[i+3] = get_exec_cookie(cpu, task);
257 }
258 }
259
260 // timer/end event
261 schedBuf[schedPos++] = SCHED_TIMER_EVENT;
262 schedBuf[schedPos++] = (int)time;
263 schedBuf[schedPos++] = (int)(time >> 32);
264
265 if (buffer)
266 *buffer = schedBuf;
267
268 return schedPos;
269}