diff options
-rw-r--r-- | LICENSE | 339 | ||||
-rw-r--r-- | Makefile | 25 | ||||
-rw-r--r-- | README_Streamline.txt | 117 | ||||
-rw-r--r-- | gator.h | 38 | ||||
-rw-r--r-- | gator_annotate.c | 135 | ||||
-rw-r--r-- | gator_backtrace.c | 68 | ||||
-rw-r--r-- | gator_cookies.c | 234 | ||||
-rw-r--r-- | gator_events.c | 37 | ||||
-rw-r--r-- | gator_events.h | 19 | ||||
-rw-r--r-- | gator_events_armv6.c | 250 | ||||
-rw-r--r-- | gator_events_armv7.c | 427 | ||||
-rw-r--r-- | gator_events_block.c | 180 | ||||
-rw-r--r-- | gator_events_irq.c | 178 | ||||
-rw-r--r-- | gator_events_meminfo.c | 203 | ||||
-rw-r--r-- | gator_events_sched.c | 123 | ||||
-rw-r--r-- | gator_fs.c | 270 | ||||
-rw-r--r-- | gator_main.c | 990 | ||||
-rw-r--r-- | gator_trace.h | 26 | ||||
-rw-r--r-- | gator_trace_sched.c | 269 |
19 files changed, 3928 insertions, 0 deletions
@@ -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 | ||
12 | freedom to share and change it. By contrast, the GNU General Public | ||
13 | License is intended to guarantee your freedom to share and change free | ||
14 | software--to make sure the software is free for all its users. This | ||
15 | General Public License applies to most of the Free Software | ||
16 | Foundation's software and to any other program whose authors commit to | ||
17 | using it. (Some other Free Software Foundation software is covered by | ||
18 | the GNU Lesser General Public License instead.) You can apply it to | ||
19 | your programs, too. | ||
20 | |||
21 | When we speak of free software, we are referring to freedom, not | ||
22 | price. Our General Public Licenses are designed to make sure that you | ||
23 | have the freedom to distribute copies of free software (and charge for | ||
24 | this service if you wish), that you receive source code or can get it | ||
25 | if you want it, that you can change the software or use pieces of it | ||
26 | in 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 | ||
29 | anyone to deny you these rights or to ask you to surrender the rights. | ||
30 | These restrictions translate to certain responsibilities for you if you | ||
31 | distribute copies of the software, or if you modify it. | ||
32 | |||
33 | For example, if you distribute copies of such a program, whether | ||
34 | gratis or for a fee, you must give the recipients all the rights that | ||
35 | you have. You must make sure that they, too, receive or can get the | ||
36 | source code. And you must show them these terms so they know their | ||
37 | rights. | ||
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, | ||
41 | distribute and/or modify the software. | ||
42 | |||
43 | Also, for each author's protection and ours, we want to make certain | ||
44 | that everyone understands that there is no warranty for this free | ||
45 | software. If the software is modified by someone else and passed on, we | ||
46 | want its recipients to know that what they have is not the original, so | ||
47 | that any problems introduced by others will not reflect on the original | ||
48 | authors' reputations. | ||
49 | |||
50 | Finally, any free program is threatened constantly by software | ||
51 | patents. We wish to avoid the danger that redistributors of a free | ||
52 | program will individually obtain patent licenses, in effect making the | ||
53 | program proprietary. To prevent this, we have made it clear that any | ||
54 | patent must be licensed for everyone's free use or not licensed at all. | ||
55 | |||
56 | The precise terms and conditions for copying, distribution and | ||
57 | modification 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 | ||
63 | a notice placed by the copyright holder saying it may be distributed | ||
64 | under the terms of this General Public License. The "Program", below, | ||
65 | refers to any such program or work, and a "work based on the Program" | ||
66 | means either the Program or any derivative work under copyright law: | ||
67 | that is to say, a work containing the Program or a portion of it, | ||
68 | either verbatim or with modifications and/or translated into another | ||
69 | language. (Hereinafter, translation is included without limitation in | ||
70 | the term "modification".) Each licensee is addressed as "you". | ||
71 | |||
72 | Activities other than copying, distribution and modification are not | ||
73 | covered by this License; they are outside its scope. The act of | ||
74 | running the Program is not restricted, and the output from the Program | ||
75 | is covered only if its contents constitute a work based on the | ||
76 | Program (independent of having been made by running the Program). | ||
77 | Whether that is true depends on what the Program does. | ||
78 | |||
79 | 1. You may copy and distribute verbatim copies of the Program's | ||
80 | source code as you receive it, in any medium, provided that you | ||
81 | conspicuously and appropriately publish on each copy an appropriate | ||
82 | copyright notice and disclaimer of warranty; keep intact all the | ||
83 | notices that refer to this License and to the absence of any warranty; | ||
84 | and give any other recipients of the Program a copy of this License | ||
85 | along with the Program. | ||
86 | |||
87 | You may charge a fee for the physical act of transferring a copy, and | ||
88 | you 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 | ||
91 | of it, thus forming a work based on the Program, and copy and | ||
92 | distribute such modifications or work under the terms of Section 1 | ||
93 | above, 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 | |||
114 | These requirements apply to the modified work as a whole. If | ||
115 | identifiable sections of that work are not derived from the Program, | ||
116 | and can be reasonably considered independent and separate works in | ||
117 | themselves, then this License, and its terms, do not apply to those | ||
118 | sections when you distribute them as separate works. But when you | ||
119 | distribute the same sections as part of a whole which is a work based | ||
120 | on the Program, the distribution of the whole must be on the terms of | ||
121 | this License, whose permissions for other licensees extend to the | ||
122 | entire whole, and thus to each and every part regardless of who wrote it. | ||
123 | |||
124 | Thus, it is not the intent of this section to claim rights or contest | ||
125 | your rights to work written entirely by you; rather, the intent is to | ||
126 | exercise the right to control the distribution of derivative or | ||
127 | collective works based on the Program. | ||
128 | |||
129 | In addition, mere aggregation of another work not based on the Program | ||
130 | with the Program (or with a work based on the Program) on a volume of | ||
131 | a storage or distribution medium does not bring the other work under | ||
132 | the scope of this License. | ||
133 | |||
134 | 3. You may copy and distribute the Program (or a work based on it, | ||
135 | under Section 2) in object code or executable form under the terms of | ||
136 | Sections 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 | |||
155 | The source code for a work means the preferred form of the work for | ||
156 | making modifications to it. For an executable work, complete source | ||
157 | code means all the source code for all modules it contains, plus any | ||
158 | associated interface definition files, plus the scripts used to | ||
159 | control compilation and installation of the executable. However, as a | ||
160 | special exception, the source code distributed need not include | ||
161 | anything that is normally distributed (in either source or binary | ||
162 | form) with the major components (compiler, kernel, and so on) of the | ||
163 | operating system on which the executable runs, unless that component | ||
164 | itself accompanies the executable. | ||
165 | |||
166 | If distribution of executable or object code is made by offering | ||
167 | access to copy from a designated place, then offering equivalent | ||
168 | access to copy the source code from the same place counts as | ||
169 | distribution of the source code, even though third parties are not | ||
170 | compelled to copy the source along with the object code. | ||
171 | |||
172 | 4. You may not copy, modify, sublicense, or distribute the Program | ||
173 | except as expressly provided under this License. Any attempt | ||
174 | otherwise to copy, modify, sublicense or distribute the Program is | ||
175 | void, and will automatically terminate your rights under this License. | ||
176 | However, parties who have received copies, or rights, from you under | ||
177 | this License will not have their licenses terminated so long as such | ||
178 | parties remain in full compliance. | ||
179 | |||
180 | 5. You are not required to accept this License, since you have not | ||
181 | signed it. However, nothing else grants you permission to modify or | ||
182 | distribute the Program or its derivative works. These actions are | ||
183 | prohibited by law if you do not accept this License. Therefore, by | ||
184 | modifying or distributing the Program (or any work based on the | ||
185 | Program), you indicate your acceptance of this License to do so, and | ||
186 | all its terms and conditions for copying, distributing or modifying | ||
187 | the Program or works based on it. | ||
188 | |||
189 | 6. Each time you redistribute the Program (or any work based on the | ||
190 | Program), the recipient automatically receives a license from the | ||
191 | original licensor to copy, distribute or modify the Program subject to | ||
192 | these terms and conditions. You may not impose any further | ||
193 | restrictions on the recipients' exercise of the rights granted herein. | ||
194 | You are not responsible for enforcing compliance by third parties to | ||
195 | this License. | ||
196 | |||
197 | 7. If, as a consequence of a court judgment or allegation of patent | ||
198 | infringement or for any other reason (not limited to patent issues), | ||
199 | conditions are imposed on you (whether by court order, agreement or | ||
200 | otherwise) that contradict the conditions of this License, they do not | ||
201 | excuse you from the conditions of this License. If you cannot | ||
202 | distribute so as to satisfy simultaneously your obligations under this | ||
203 | License and any other pertinent obligations, then as a consequence you | ||
204 | may not distribute the Program at all. For example, if a patent | ||
205 | license would not permit royalty-free redistribution of the Program by | ||
206 | all those who receive copies directly or indirectly through you, then | ||
207 | the only way you could satisfy both it and this License would be to | ||
208 | refrain entirely from distribution of the Program. | ||
209 | |||
210 | If any portion of this section is held invalid or unenforceable under | ||
211 | any particular circumstance, the balance of the section is intended to | ||
212 | apply and the section as a whole is intended to apply in other | ||
213 | circumstances. | ||
214 | |||
215 | It is not the purpose of this section to induce you to infringe any | ||
216 | patents or other property right claims or to contest validity of any | ||
217 | such claims; this section has the sole purpose of protecting the | ||
218 | integrity of the free software distribution system, which is | ||
219 | implemented by public license practices. Many people have made | ||
220 | generous contributions to the wide range of software distributed | ||
221 | through that system in reliance on consistent application of that | ||
222 | system; it is up to the author/donor to decide if he or she is willing | ||
223 | to distribute software through any other system and a licensee cannot | ||
224 | impose that choice. | ||
225 | |||
226 | This section is intended to make thoroughly clear what is believed to | ||
227 | be a consequence of the rest of this License. | ||
228 | |||
229 | 8. If the distribution and/or use of the Program is restricted in | ||
230 | certain countries either by patents or by copyrighted interfaces, the | ||
231 | original copyright holder who places the Program under this License | ||
232 | may add an explicit geographical distribution limitation excluding | ||
233 | those countries, so that distribution is permitted only in or among | ||
234 | countries not thus excluded. In such case, this License incorporates | ||
235 | the limitation as if written in the body of this License. | ||
236 | |||
237 | 9. The Free Software Foundation may publish revised and/or new versions | ||
238 | of the General Public License from time to time. Such new versions will | ||
239 | be similar in spirit to the present version, but may differ in detail to | ||
240 | address new problems or concerns. | ||
241 | |||
242 | Each version is given a distinguishing version number. If the Program | ||
243 | specifies a version number of this License which applies to it and "any | ||
244 | later version", you have the option of following the terms and conditions | ||
245 | either of that version or of any later version published by the Free | ||
246 | Software Foundation. If the Program does not specify a version number of | ||
247 | this License, you may choose any version ever published by the Free Software | ||
248 | Foundation. | ||
249 | |||
250 | 10. If you wish to incorporate parts of the Program into other free | ||
251 | programs whose distribution conditions are different, write to the author | ||
252 | to ask for permission. For software which is copyrighted by the Free | ||
253 | Software Foundation, write to the Free Software Foundation; we sometimes | ||
254 | make exceptions for this. Our decision will be guided by the two goals | ||
255 | of preserving the free status of all derivatives of our free software and | ||
256 | of 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 | ||
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN | ||
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES | ||
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED | ||
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS | ||
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE | ||
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, | ||
268 | REPAIR OR CORRECTION. | ||
269 | |||
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | ||
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR | ||
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, | ||
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING | ||
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED | ||
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY | ||
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER | ||
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE | ||
278 | POSSIBILITY 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 | ||
285 | possible use to the public, the best way to achieve this is to make it | ||
286 | free 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 | ||
289 | to attach them to the start of each source file to most effectively | ||
290 | convey the exclusion of warranty; and each file should have at least | ||
291 | the "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 | |||
310 | Also add information on how to contact you by electronic and paper mail. | ||
311 | |||
312 | If the program is interactive, make it output a short notice like this | ||
313 | when 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 | |||
320 | The hypothetical commands `show w' and `show c' should show the appropriate | ||
321 | parts of the General Public License. Of course, the commands you use may | ||
322 | be called something other than `show w' and `show c'; they could even be | ||
323 | mouse-clicks or menu items--whatever suits your program. | ||
324 | |||
325 | You should also get your employer (if you work as a programmer) or your | ||
326 | school, if any, to sign a "copyright disclaimer" for the program, if | ||
327 | necessary. 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 | |||
335 | This General Public License does not permit incorporating your program into | ||
336 | proprietary programs. If your program is a subroutine library, you may | ||
337 | consider it more useful to permit linking proprietary applications with the | ||
338 | library. If this is what you want to do, use the GNU Lesser General | ||
339 | Public 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 @@ | |||
1 | ifneq ($(KERNELRELEASE),) | ||
2 | |||
3 | obj-m := gator.o | ||
4 | |||
5 | gator-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 | |||
13 | else | ||
14 | |||
15 | all: | ||
16 | @echo | ||
17 | @echo "usage:" | ||
18 | @echo " make -C <kernel_build_dir> M=\`pwd\` ARCH=arm CROSS_COMPILE=<...> modules" | ||
19 | @echo | ||
20 | $(error) | ||
21 | |||
22 | clean: | ||
23 | rm -f *.o .*.cmd */*.o */.*.cmd modules.order Module.symvers gator.ko gator.mod.c | ||
24 | |||
25 | endif | ||
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 | |||
4 | Instructions on setting up ARM Streamline on the target. | ||
5 | The gator driver and gator daemon are required to run on the ARM linux target in order for ARM Streamline to operate. | ||
6 | The 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 | |||
10 | cd into the root source dir of the linux kernel | ||
11 | make ARCH=arm CROSS_COMPILE=${CROSS_TOOLS}/bin/arm-none-linux-gnueabi- <platform_defconfig> (choose the appropriate configuration for your board) | ||
12 | make ARCH=arm CROSS_COMPILE=${CROSS_TOOLS}/bin/arm-none-linux-gnueabi- menuconfig | ||
13 | |||
14 | Required 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 | |||
23 | The "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 | |||
25 | Optional Kernel Changes (depending on the kernel version, the location of these configuration settings within menuconfig may be different) | ||
26 | Note: 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 | |||
30 | make -j5 ARCH=arm CROSS_COMPILE=${CROSS_TOOLS}/bin/arm-none-linux-gnueabi- uImage | ||
31 | |||
32 | *** Building the gator module *** | ||
33 | |||
34 | To 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 | ||
39 | for 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 | ||
41 | If successful, a gator.ko module should be generated | ||
42 | |||
43 | *** Compiling an application or shared library *** | ||
44 | |||
45 | Recommended 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 | |||
52 | Load the kernel onto the target and copy gatord and gator.ko into the target's filesystem. | ||
53 | gatord is located in <installdir>/arm/armv5t/. | ||
54 | Ensure gatord has execute permissions | ||
55 | chmod +x gatord | ||
56 | gator.ko must be located in the same directory as gatord on the target. | ||
57 | With root privileges, run the daemon | ||
58 | sudo ./gatord & | ||
59 | |||
60 | *** Profiling the kernel (optional) *** | ||
61 | |||
62 | make ARCH=arm CROSS_COMPILE=$(CROSS_TOOLS}/bin/arm-none-linux-gnueabi- menuconfig | ||
63 | - Kernel Hacking | ||
64 | - [*] Compile the kernel with debug info | ||
65 | |||
66 | make -j5 ARCH=arm CROSS_COMPILE=${CROSS_TOOLS}/bin/arm-none-linux-gnueabi- uImage | ||
67 | Use vmlinux as the image for debug symbols in Streamline. | ||
68 | Drivers may be profiled using this method by statically linking the driver into the kernel image. | ||
69 | Note that the gator driver does not perform kernel call stack recording. | ||
70 | |||
71 | *** Automatically start gator on boot (optional) *** | ||
72 | |||
73 | cd /etc/init.d | ||
74 | vi rungator.sh | ||
75 | #!/bin/bash | ||
76 | /path/to/gatord & | ||
77 | update-rc.d rungator.sh defaults | ||
78 | |||
79 | *** Driver Sources *** | ||
80 | |||
81 | Gator 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 | |||
97 | Oprofile 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 | |||
110 | Modified 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 | |||
117 | For license information, please see the file LICENSE. | ||
@@ -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 | ******************************************************************************/ | ||
12 | int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root, | ||
13 | char const *name, const struct file_operations *fops, int perm); | ||
14 | |||
15 | struct dentry *gatorfs_mkdir(struct super_block *sb, | ||
16 | struct dentry *root, char const *name); | ||
17 | |||
18 | int gatorfs_create_ulong(struct super_block *sb, struct dentry *root, | ||
19 | char const *name, unsigned long *val); | ||
20 | |||
21 | int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root, | ||
22 | char const *name, unsigned long *val); | ||
23 | |||
24 | /****************************************************************************** | ||
25 | * Events | ||
26 | ******************************************************************************/ | ||
27 | struct __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 | |||
36 | typedef struct __gator_interface gator_interface; | ||
37 | |||
38 | int 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 | ||
19 | static DEFINE_SPINLOCK(annotate_lock); | ||
20 | static char *annotateBuf; | ||
21 | static char *annotateBuf0; | ||
22 | static char *annotateBuf1; | ||
23 | static int annotatePos; | ||
24 | static int annotateSel; | ||
25 | |||
26 | static 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 | |||
37 | static 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 | |||
76 | static const struct file_operations annotate_fops = { | ||
77 | .write = annotate_write | ||
78 | }; | ||
79 | |||
80 | int 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 | |||
86 | int gator_annotate_init(void) | ||
87 | { | ||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | int 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 | |||
102 | void 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 | |||
113 | int 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 | */ | ||
13 | struct frame_tail_eabi { | ||
14 | unsigned long fp; // points to prev_lr | ||
15 | unsigned long lr; | ||
16 | }; | ||
17 | |||
18 | static 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 | |||
13 | static DEFINE_PER_CPU(uint32_t, cookie_next_key); | ||
14 | static DEFINE_PER_CPU(uint32_t, cookie_prev_text); | ||
15 | static DEFINE_PER_CPU(uint32_t, cookie_prev_value); | ||
16 | static DEFINE_PER_CPU(uint64_t *, cookie_keys); | ||
17 | static DEFINE_PER_CPU(uint32_t *, cookie_values); | ||
18 | |||
19 | static uint32_t *gator_crc32_table; | ||
20 | |||
21 | static 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 | |||
30 | static 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 | */ | ||
49 | static 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 | */ | ||
77 | static 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 | |||
92 | static 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 | |||
127 | output: | ||
128 | per_cpu(cookie_prev_text, cpu) = (uint32_t)text; | ||
129 | per_cpu(cookie_prev_value, cpu) = cookie; | ||
130 | return cookie; | ||
131 | } | ||
132 | |||
133 | static 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 | |||
154 | static 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 | |||
184 | static 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 | |||
220 | static 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 | |||
15 | extern int gator_events_armv6_install(gator_interface *gi); | ||
16 | extern int gator_events_armv7_install(gator_interface *gi); | ||
17 | extern int gator_events_irq_install(gator_interface *gi); | ||
18 | extern int gator_events_sched_install(gator_interface *gi); | ||
19 | extern int gator_events_block_install(gator_interface *gi); | ||
20 | extern int gator_events_meminfo_install(gator_interface *gi); | ||
21 | |||
22 | static 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 | |||
10 | struct __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 | |||
19 | typedef 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 | |||
23 | static const char *pmnc_name; | ||
24 | |||
25 | extern 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 | |||
42 | static int pmnc_count = 0; | ||
43 | static unsigned long pmnc_enabled[CNTMAX]; | ||
44 | static unsigned long pmnc_event[CNTMAX]; | ||
45 | static unsigned long pmnc_key[CNTMAX]; | ||
46 | |||
47 | static DEFINE_PER_CPU(int[CNTMAX], perfPrev); | ||
48 | static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); | ||
49 | |||
50 | static 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 | |||
57 | static 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 | |||
64 | static 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 | |||
80 | int 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 | |||
108 | static 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 | |||
122 | static 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 | |||
165 | static 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 | |||
172 | static 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 | |||
185 | static 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 | |||
192 | static 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 | |||
231 | int 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 | |||
21 | static const char *pmnc_name; | ||
22 | static int pmnc_count; | ||
23 | |||
24 | extern 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 | |||
91 | static unsigned long pmnc_enabled[CNTMAX]; | ||
92 | static unsigned long pmnc_event[CNTMAX]; | ||
93 | static unsigned long pmnc_key[CNTMAX]; | ||
94 | |||
95 | static DEFINE_PER_CPU(int[CNTMAX], perfPrev); | ||
96 | static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); | ||
97 | |||
98 | static 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 | |||
104 | static 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 | |||
111 | static 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 | |||
118 | static 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 | |||
125 | static 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 | |||
145 | static 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 | |||
165 | static 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 | |||
185 | static 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 | |||
199 | static 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 | |||
214 | static 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 | |||
222 | static 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 | |||
247 | static 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 | |||
273 | static 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 | |||
287 | static 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 | |||
342 | static 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 | |||
349 | static 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 | |||
360 | static 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 | |||
367 | static 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 | |||
403 | int 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 | |||
30 | static ulong block_rq_wr_enabled; | ||
31 | static ulong block_rq_rd_enabled; | ||
32 | static ulong block_rq_wr_key; | ||
33 | static ulong block_rq_rd_key; | ||
34 | static DEFINE_PER_CPU(int[BLOCK_TOTAL], blockCnt); | ||
35 | static DEFINE_PER_CPU(int[BLOCK_TOTAL * 2], blockGet); | ||
36 | static DEFINE_PER_CPU(bool, new_data_avail); | ||
37 | |||
38 | GATOR_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 | |||
65 | static 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 | |||
88 | static 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 | |||
101 | static 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 | ||
117 | fail_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 | |||
123 | static 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 | |||
133 | static 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 | |||
173 | int 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 | |||
24 | static ulong hardirq_enabled; | ||
25 | static ulong softirq_enabled; | ||
26 | static ulong hardirq_key; | ||
27 | static ulong softirq_key; | ||
28 | static DEFINE_PER_CPU(int[TOTALIRQ], irqCnt); | ||
29 | static DEFINE_PER_CPU(int[TOTALIRQ], irqPrev); | ||
30 | static DEFINE_PER_CPU(int[TOTALIRQ * 2], irqGet); | ||
31 | |||
32 | GATOR_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 | |||
44 | GATOR_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 | |||
56 | static 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 | |||
79 | static 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 | |||
92 | static 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 | ||
113 | fail_softirq_exit: | ||
114 | if (hardirq_enabled) | ||
115 | GATOR_UNREGISTER_TRACE(irq_handler_exit); | ||
116 | fail_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 | |||
122 | static 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 | |||
134 | static 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 | |||
171 | int 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 | |||
25 | static ulong meminfo_global_enabled; | ||
26 | static ulong meminfo_enabled[MEMINFO_TOTAL]; | ||
27 | static ulong meminfo_key[MEMINFO_TOTAL]; | ||
28 | static int meminfo_buffer[MEMINFO_TOTAL * 2]; | ||
29 | static int meminfo_length = 0; | ||
30 | static unsigned int mem_event = 0; | ||
31 | static bool new_data_avail; | ||
32 | |||
33 | static void wq_sched_handler(struct work_struct *wsptr); | ||
34 | |||
35 | DECLARE_WORK(work, wq_sched_handler); | ||
36 | |||
37 | GATOR_DEFINE_PROBE(mm_page_free_direct, TP_PROTO(struct page *page, unsigned int order)) { | ||
38 | mem_event++; | ||
39 | } | ||
40 | |||
41 | GATOR_DEFINE_PROBE(mm_pagevec_free, TP_PROTO(struct page *page, int cold)) { | ||
42 | mem_event++; | ||
43 | } | ||
44 | |||
45 | GATOR_DEFINE_PROBE(mm_page_alloc, TP_PROTO(struct page *page, unsigned int order, gfp_t gfp_flags, int migratetype)) { | ||
46 | mem_event++; | ||
47 | } | ||
48 | |||
49 | static 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 | |||
78 | static 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 | |||
92 | static 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 | |||
115 | mm_page_alloc_exit: | ||
116 | GATOR_UNREGISTER_TRACE(mm_pagevec_free); | ||
117 | mm_pagevec_free_exit: | ||
118 | GATOR_UNREGISTER_TRACE(mm_page_free_direct); | ||
119 | mm_page_free_direct_exit: | ||
120 | return -1; | ||
121 | } | ||
122 | |||
123 | static 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 | ||
140 | static 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 | |||
173 | static 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 | |||
196 | int 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 | |||
23 | static ulong sched_switch_enabled; | ||
24 | static ulong sched_switch_key; | ||
25 | static DEFINE_PER_CPU(int[SCHED_TOTAL], schedCnt); | ||
26 | static DEFINE_PER_CPU(int[SCHED_TOTAL * 2], schedGet); | ||
27 | |||
28 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) | ||
29 | GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next)) | ||
30 | #else | ||
31 | GATOR_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 | |||
43 | static 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 | |||
58 | static 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 | |||
68 | static 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 | ||
79 | sched_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 | |||
85 | static 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 | |||
94 | static 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 | |||
116 | int 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 | ||
21 | DEFINE_SPINLOCK(gatorfs_lock); | ||
22 | |||
23 | void gator_op_create_files(struct super_block *sb, struct dentry *root); | ||
24 | |||
25 | static 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 | |||
36 | static const struct super_operations s_ops = { | ||
37 | .statfs = simple_statfs, | ||
38 | .drop_inode = generic_delete_inode, | ||
39 | }; | ||
40 | |||
41 | ssize_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 | |||
46 | ssize_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 | |||
55 | int 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 | |||
77 | static 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 | |||
83 | static 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 | |||
98 | static 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 | |||
105 | static const struct file_operations ulong_fops = { | ||
106 | .read = ulong_read_file, | ||
107 | .write = ulong_write_file, | ||
108 | .open = default_open, | ||
109 | }; | ||
110 | |||
111 | static const struct file_operations ulong_ro_fops = { | ||
112 | .read = ulong_read_file, | ||
113 | .open = default_open, | ||
114 | }; | ||
115 | |||
116 | static 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 | |||
136 | int 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 | |||
148 | int 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 | |||
160 | static 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 | |||
166 | static const struct file_operations atomic_ro_fops = { | ||
167 | .read = atomic_read_file, | ||
168 | .open = default_open, | ||
169 | }; | ||
170 | |||
171 | int 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 | |||
183 | int 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 | |||
191 | int 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 | |||
199 | struct 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 | |||
219 | static 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 | |||
249 | static 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 | |||
255 | static 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 | |||
262 | int __init gatorfs_register(void) | ||
263 | { | ||
264 | return register_filesystem(&gatorfs_type); | ||
265 | } | ||
266 | |||
267 | void 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 | |||
10 | static 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 | ******************************************************************************/ | ||
74 | static unsigned long gator_buffer_size; | ||
75 | static unsigned long gator_backtrace_depth; | ||
76 | |||
77 | static unsigned long gator_started; | ||
78 | static unsigned long gator_buffer_opened; | ||
79 | static unsigned long gator_timer_count; | ||
80 | static unsigned long gator_dump; | ||
81 | static DEFINE_MUTEX(start_mutex); | ||
82 | static DEFINE_MUTEX(gator_buffer_mutex); | ||
83 | static DECLARE_WAIT_QUEUE_HEAD(gator_buffer_wait); | ||
84 | /* atomic_t because wait_event checks it outside of gator_buffer_mutex */ | ||
85 | static DEFINE_PER_CPU(atomic_t, gator_buffer_ready); | ||
86 | static DEFINE_PER_CPU(int, gator_tick); | ||
87 | static DEFINE_PER_CPU(int, gator_first_time); | ||
88 | |||
89 | /****************************************************************************** | ||
90 | * Prototypes | ||
91 | ******************************************************************************/ | ||
92 | static void gator_buffer_write_packed_int(int cpu, unsigned int x); | ||
93 | static void gator_buffer_write_string(int cpu, char *x); | ||
94 | static void gator_add_trace(int cpu, unsigned int address); | ||
95 | static 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__) | ||
111 | u32 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 | ******************************************************************************/ | ||
121 | static uint32_t use_buffer_size; | ||
122 | static uint32_t use_buffer_mask; | ||
123 | static DEFINE_PER_CPU(int, use_buffer_seq); | ||
124 | static DEFINE_PER_CPU(int, use_buffer_read); | ||
125 | static DEFINE_PER_CPU(int, use_buffer_write); | ||
126 | static DEFINE_PER_CPU(int, use_buffer_commit); | ||
127 | static DEFINE_PER_CPU(char *, use_buffer); | ||
128 | |||
129 | static 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 | |||
169 | static 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 | |||
184 | static 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 | |||
191 | static 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 | |||
198 | static 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 | |||
209 | static 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 | |||
231 | static 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 | |||
244 | static 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 | |||
275 | static 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 | |||
285 | static 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 | ******************************************************************************/ | ||
306 | static gator_interface *gi = NULL; | ||
307 | |||
308 | static 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 | ******************************************************************************/ | ||
367 | DEFINE_PER_CPU(struct hrtimer, percpu_hrtimer); | ||
368 | extern void gator_timer_stop(void); | ||
369 | static int hrtimer_running; | ||
370 | static ktime_t profiling_interval; | ||
371 | static atomic_t num_active_timers; | ||
372 | |||
373 | static 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 | |||
380 | int gator_timer_init(void) | ||
381 | { | ||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | static 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 | |||
399 | int 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 | |||
428 | void __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 | |||
435 | void 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 | |||
448 | static 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 | ******************************************************************************/ | ||
462 | int 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 | |||
496 | static 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 | |||
524 | static 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 | |||
546 | timer_failure: | ||
547 | gator_trace_sched_stop(); | ||
548 | sched_failure: | ||
549 | gator_annotate_stop(); | ||
550 | annotate_failure: | ||
551 | events_failure: | ||
552 | for (f = gi; f != i; f = f->next) { | ||
553 | f->stop(); | ||
554 | } | ||
555 | |||
556 | return -1; | ||
557 | } | ||
558 | |||
559 | static 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 | |||
576 | static 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") */ | ||
591 | static 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 | |||
622 | setup_error: | ||
623 | mutex_unlock(&start_mutex); | ||
624 | return err; | ||
625 | } | ||
626 | |||
627 | /* Actually start profiling (echo 1>/dev/gator/enable) */ | ||
628 | static 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 */ | ||
647 | static 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 | |||
673 | static 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 | |||
693 | static 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 | |||
709 | static 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 | |||
714 | static 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 | |||
736 | static const struct file_operations enable_fops = { | ||
737 | .read = enable_read, | ||
738 | .write = enable_write, | ||
739 | }; | ||
740 | |||
741 | static 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 | |||
769 | fail: | ||
770 | dcookie_unregister(file->private_data); | ||
771 | out: | ||
772 | __clear_bit_unlock(0, &gator_buffer_opened); | ||
773 | return err; | ||
774 | } | ||
775 | |||
776 | static 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 | |||
789 | static 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 | |||
799 | static 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 | |||
869 | out: | ||
870 | mutex_unlock(&gator_buffer_mutex); | ||
871 | return retval; | ||
872 | } | ||
873 | |||
874 | const struct file_operations gator_event_buffer_fops = { | ||
875 | .open = event_buffer_open, | ||
876 | .release = event_buffer_release, | ||
877 | .read = event_buffer_read, | ||
878 | }; | ||
879 | |||
880 | static 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 | |||
886 | static 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 | |||
905 | static const struct file_operations depth_fops = { | ||
906 | .read = depth_read, | ||
907 | .write = depth_write | ||
908 | }; | ||
909 | |||
910 | static const char gator_cpu_type[] = "gator"; | ||
911 | |||
912 | static 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 | |||
917 | static const struct file_operations cpu_type_fops = { | ||
918 | .read = cpu_type_read, | ||
919 | }; | ||
920 | |||
921 | void 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 | ******************************************************************************/ | ||
953 | static int gator_initialized; | ||
954 | |||
955 | static 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 | |||
973 | static 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 | |||
985 | module_init(gator_module_init); | ||
986 | module_exit(gator_module_exit); | ||
987 | |||
988 | MODULE_LICENSE("GPL"); | ||
989 | MODULE_AUTHOR("ARM Ltd"); | ||
990 | MODULE_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 | |||
33 | static DEFINE_PER_CPU(int *[2], theSchedBuf); | ||
34 | static DEFINE_PER_CPU(int, theSchedSel); | ||
35 | static DEFINE_PER_CPU(int, theSchedPos); | ||
36 | static DEFINE_PER_CPU(int, theSchedErr); | ||
37 | |||
38 | static 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) | ||
79 | GATOR_DEFINE_PROBE(sched_wait_task, TP_PROTO(struct rq *rq, struct task_struct *p)) | ||
80 | #else | ||
81 | GATOR_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) | ||
88 | GATOR_DEFINE_PROBE(sched_wakeup, TP_PROTO(struct rq *rq, struct task_struct *p, int success)) | ||
89 | #else | ||
90 | GATOR_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) | ||
98 | GATOR_DEFINE_PROBE(sched_wakeup_new, TP_PROTO(struct rq *rq, struct task_struct *p, int success)) | ||
99 | #else | ||
100 | GATOR_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) | ||
108 | GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next)) | ||
109 | #else | ||
110 | GATOR_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 | |||
116 | GATOR_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 | |||
121 | GATOR_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 | |||
126 | GATOR_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 | |||
131 | GATOR_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 | |||
136 | GATOR_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 | |||
141 | int gator_trace_sched_init(void) | ||
142 | { | ||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | int 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 | ||
184 | fail_sched_process_fork: | ||
185 | GATOR_UNREGISTER_TRACE(sched_process_wait); | ||
186 | fail_sched_process_wait: | ||
187 | GATOR_UNREGISTER_TRACE(sched_process_exit); | ||
188 | fail_sched_process_exit: | ||
189 | GATOR_UNREGISTER_TRACE(sched_process_free); | ||
190 | fail_sched_process_free: | ||
191 | GATOR_UNREGISTER_TRACE(sched_migrate_task); | ||
192 | fail_sched_migrate_task: | ||
193 | GATOR_UNREGISTER_TRACE(sched_switch); | ||
194 | fail_sched_switch: | ||
195 | GATOR_UNREGISTER_TRACE(sched_wakeup_new); | ||
196 | fail_sched_wakeup_new: | ||
197 | GATOR_UNREGISTER_TRACE(sched_wakeup); | ||
198 | fail_sched_wakeup: | ||
199 | GATOR_UNREGISTER_TRACE(sched_wait_task); | ||
200 | fail_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 | |||
206 | void 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 | |||
228 | int 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 | } | ||