From a3a50f5d7331c6294a81039d1eae731ed5682521 Mon Sep 17 00:00:00 2001 From: Drew Richardson Date: Wed, 2 Feb 2011 12:00:00 -0800 Subject: gator: Version 5.4 Signed-off-by: Drew Richardson --- .gitignore | 7 + README_Streamline.txt | 81 ++++ driver/LICENSE | 339 +++++++++++++ driver/Makefile | 26 + driver/gator.h | 72 +++ driver/gator_annotate.c | 135 ++++++ driver/gator_backtrace.c | 68 +++ driver/gator_cookies.c | 224 +++++++++ driver/gator_events.c | 40 ++ driver/gator_events_armv6.c | 233 +++++++++ driver/gator_events_armv7.c | 411 ++++++++++++++++ driver/gator_events_block.c | 174 +++++++ driver/gator_events_irq.c | 174 +++++++ driver/gator_events_meminfo.c | 197 ++++++++ driver/gator_events_net.c | 176 +++++++ driver/gator_events_sched.c | 116 +++++ driver/gator_fs.c | 270 +++++++++++ driver/gator_main.c | 1071 +++++++++++++++++++++++++++++++++++++++++ driver/gator_trace_sched.c | 263 ++++++++++ 19 files changed, 4077 insertions(+) create mode 100644 .gitignore create mode 100644 README_Streamline.txt create mode 100644 driver/LICENSE create mode 100644 driver/Makefile create mode 100644 driver/gator.h create mode 100644 driver/gator_annotate.c create mode 100644 driver/gator_backtrace.c create mode 100644 driver/gator_cookies.c create mode 100644 driver/gator_events.c create mode 100644 driver/gator_events_armv6.c create mode 100644 driver/gator_events_armv7.c create mode 100644 driver/gator_events_block.c create mode 100644 driver/gator_events_irq.c create mode 100644 driver/gator_events_meminfo.c create mode 100644 driver/gator_events_net.c create mode 100644 driver/gator_events_sched.c create mode 100644 driver/gator_fs.c create mode 100644 driver/gator_main.c create mode 100644 driver/gator_trace_sched.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8365686 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +driver/*.cmd +driver/*.mod.c +driver/*.o +driver/.tmp_versions +driver/Module.symvers +driver/gator.ko +driver/modules.order diff --git a/README_Streamline.txt b/README_Streamline.txt new file mode 100644 index 0000000..484aed3 --- /dev/null +++ b/README_Streamline.txt @@ -0,0 +1,81 @@ + +*** Purpose *** + +Instructions on setting up ARM Streamline on the target. +The gator driver and gator daemon are required to run on the ARM linux target in order for ARM Streamline to operate. +The driver should be built as a module and the daemon must run with root permissions on the target. + +*** Preparing and building the kernel *** + +cd into the root source dir of the linux kernel +make ARCH=arm CROSS_COMPILE=${CROSS_TOOLS}/bin/arm-none-linux-gnueabi- (choose the appropriate configuration for your board) +make ARCH=arm CROSS_COMPILE=${CROSS_TOOLS}/bin/arm-none-linux-gnueabi- menuconfig + +Required Kernel Changes (depending on the kernel version, the location of these configuration settings within menuconfig may be different) +- General Setup + - [*] Profiling Support +- Kernel hacking + - [*] Tracers + - [*] Trace process context switches and events +- Kernel Features + - [*] High Resolution Timer Support + +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. + +Optional Kernel Changes (depending on the kernel version, the location of these configuration settings within menuconfig may be different) +Note: Configurations may not be supported on all targets +- System Type + - [*] debugging peripherals (enable core performance counters on supported SoCs) /* kernels before 2.6.35 */ + +make -j5 ARCH=arm CROSS_COMPILE=${CROSS_TOOLS}/bin/arm-none-linux-gnueabi- uImage + +*** Building the gator module *** + +To create the gator.ko module, + cd /ds-5-install-directory/arm/src + tar xzf gator-driver.tar.gz + cd gator-driver + make -C M=`pwd` ARCH=arm CROSS_COMPILE=<...> modules +for example + 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 +If successful, a gator.ko module should be generated + +*** Compiling an application or shared library *** + +Recommended compiler settings: + "-g": Debug symbols needed for best analysis results. + "-fno-inline": Speed improvement when processing the image files and most accurate analysis results. + "-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). + +*** Running gator *** + +Load the kernel onto the target and copy gatord and gator.ko into the target's filesystem. +gatord is located in /arm/armv5t/. +Ensure gatord has execute permissions + chmod +x gatord +gator.ko must be located in the same directory as gatord on the target. +With root privileges, run the daemon + sudo ./gatord & + +*** Profiling the kernel (optional) *** + +make ARCH=arm CROSS_COMPILE=$(CROSS_TOOLS}/bin/arm-none-linux-gnueabi- menuconfig +- Kernel Hacking + - [*] Compile the kernel with debug info + +make -j5 ARCH=arm CROSS_COMPILE=${CROSS_TOOLS}/bin/arm-none-linux-gnueabi- uImage +Use vmlinux as the image for debug symbols in Streamline. +Drivers may be profiled using this method by statically linking the driver into the kernel image. +Note that the gator driver does not perform kernel call stack recording. + +*** Automatically start gator on boot (optional) *** + +cd /etc/init.d +vi rungator.sh + #!/bin/bash + /path/to/gatord & +update-rc.d rungator.sh defaults + +*** GPL License *** + +For license information, please see the file LICENSE. diff --git a/driver/LICENSE b/driver/LICENSE new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/driver/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/driver/Makefile b/driver/Makefile new file mode 100644 index 0000000..0583ae3 --- /dev/null +++ b/driver/Makefile @@ -0,0 +1,26 @@ +ifneq ($(KERNELRELEASE),) + +obj-m := gator.o + +gator-objs := gator_main.o \ + gator_events_armv6.o \ + gator_events_armv7.o \ + gator_events_irq.o \ + gator_events_sched.o \ + gator_events_net.o \ + gator_events_block.o \ + gator_events_meminfo.o + +else + +all: + @echo + @echo "usage:" + @echo " make -C M=\`pwd\` ARCH=arm CROSS_COMPILE=<...> modules" + @echo + $(error) + +clean: + rm -f *.o modules.order Module.symvers gator.ko gator.mod.c + +endif diff --git a/driver/gator.h b/driver/gator.h new file mode 100644 index 0000000..f0c95bf --- /dev/null +++ b/driver/gator.h @@ -0,0 +1,72 @@ +/** + * Copyright 2010 ARM, Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef GATOR_H_ +#define GATOR_H_ + +#include +#include +#include + +/****************************************************************************** + * Filesystem + ******************************************************************************/ +int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root, + char const *name, const struct file_operations *fops, int perm); + +struct dentry *gatorfs_mkdir(struct super_block *sb, + struct dentry *root, char const *name); + +int gatorfs_create_ulong(struct super_block *sb, struct dentry *root, + char const *name, unsigned long *val); + +int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root, + char const *name, unsigned long *val); + +/****************************************************************************** + * Tracepoints + ******************************************************************************/ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) +# error Kernels prior to 2.6.32 not supported +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +# define GATOR_DEFINE_PROBE(probe_name, proto) \ + static void probe_##probe_name(PARAMS(proto)) +# define GATOR_REGISTER_TRACE(probe_name) \ + register_trace_##probe_name(probe_##probe_name) +# define GATOR_UNREGISTER_TRACE(probe_name) \ + unregister_trace_##probe_name(probe_##probe_name) +#else +# define GATOR_DEFINE_PROBE(probe_name, proto) \ + static void probe_##probe_name(void *data, PARAMS(proto)) +# define GATOR_REGISTER_TRACE(probe_name) \ + register_trace_##probe_name(probe_##probe_name, NULL) +# define GATOR_UNREGISTER_TRACE(probe_name) \ + unregister_trace_##probe_name(probe_##probe_name, NULL) +#endif + +/****************************************************************************** + * Events + ******************************************************************************/ +struct __gator_interface { + int (*create_files)(struct super_block *sb, struct dentry *root); + int (*init)(int *key); + int (*start)(void); + void (*stop)(void); + void (*online)(void); + void (*offline)(void); + int (*read)(int **buffer); + struct __gator_interface *next; +}; + +typedef struct __gator_interface gator_interface; + +int gator_event_install(int (*event_install)(gator_interface *)); + +extern unsigned long gator_net_traffic; + +#endif // GATOR_H_ diff --git a/driver/gator_annotate.c b/driver/gator_annotate.c new file mode 100644 index 0000000..56656f0 --- /dev/null +++ b/driver/gator_annotate.c @@ -0,0 +1,135 @@ +/** + * Copyright (C) ARM Limited 2010-2011. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define INSTSIZE 1024 +static DEFINE_SPINLOCK(annotate_lock); +static char *annotateBuf; +static char *annotateBuf0; +static char *annotateBuf1; +static int annotatePos; +static int annotateSel; + +static int gatorfs_copy_from_user(char* localbuf, char const __user *buf, size_t count) +{ + if (count == 0) + return 0; + + if (copy_from_user(localbuf, buf, count)) + return -EFAULT; + + return 0; +} + +static ssize_t annotate_write(struct file *file, char const __user *buf, size_t count, loff_t *offset) +{ + char tempBuffer[32]; + unsigned long flags; + int retval, remaining, size; + + if (*offset) + return -EINVAL; + + // determine size to capture + remaining = INSTSIZE - annotatePos - 24; // leave some extra space + size = count < sizeof(tempBuffer) ? count : sizeof(tempBuffer); + size = size < remaining ? size : remaining; + if (size <= 0) + return 0; + + // copy from user space + retval = gatorfs_copy_from_user(tempBuffer, buf, size); + if (retval == 0) { + // synchronize shared variables annotateBuf and annotatePos + spin_lock_irqsave(&annotate_lock, flags); + if (!annotateBuf) { + size = -EINVAL; + } else { + *(int*)&annotateBuf[annotatePos + 0] = current->pid; // thread id + *(int*)&annotateBuf[annotatePos + 4] = size; // length in bytes + memcpy(&annotateBuf[annotatePos + 8], tempBuffer, size); // data + annotatePos = annotatePos + 8 + size; // increment position + annotatePos = (annotatePos + 3) & ~3; // align to 4-byte boundary + } + spin_unlock_irqrestore(&annotate_lock, flags); + + // return the number of bytes written + retval = size; + } + + return retval; +} + +static const struct file_operations annotate_fops = { + .write = annotate_write +}; + +int gator_annotate_create_files(struct super_block *sb, struct dentry *root) +{ + annotateBuf = annotateBuf0 = annotateBuf1 = NULL; + return gatorfs_create_file_perm(sb, root, "annotate", &annotate_fops, 0666); +} + +int gator_annotate_init(void) +{ + return 0; +} + +int gator_annotate_start(void) +{ + annotatePos = annotateSel = 0; + annotateBuf0 = kmalloc(INSTSIZE, GFP_KERNEL); + annotateBuf1 = kmalloc(INSTSIZE, GFP_KERNEL); + annotateBuf = annotateBuf0; + if (!annotateBuf0 || !annotateBuf1) + return -1; + return 0; +} + +void gator_annotate_stop(void) +{ + spin_lock(&annotate_lock); + + kfree(annotateBuf0); + kfree(annotateBuf1); + annotateBuf = annotateBuf0 = annotateBuf1 = NULL; + + spin_unlock(&annotate_lock); +} + +int gator_annotate_read(int **buffer) +{ + int len; + + if (smp_processor_id() || !annotatePos || !annotateBuf) + return 0; + + annotateSel = !annotateSel; + + if (buffer) + *buffer = (int *)annotateBuf; + + spin_lock(&annotate_lock); + + len = annotatePos; + annotatePos = 0; + annotateBuf = annotateSel ? annotateBuf1 : annotateBuf0; + + spin_unlock(&annotate_lock); + + // Return number of 4-byte words + return len / 4; +} diff --git a/driver/gator_backtrace.c b/driver/gator_backtrace.c new file mode 100644 index 0000000..628a18f --- /dev/null +++ b/driver/gator_backtrace.c @@ -0,0 +1,68 @@ +/** + * Copyright (C) ARM Limited 2010-2011. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +/* + * EABI backtrace stores {fp,lr} on the stack. + */ +struct frame_tail_eabi { + unsigned long fp; // points to prev_lr + unsigned long lr; +}; + +static void arm_backtrace_eabi(int cpu, struct pt_regs * const regs, unsigned int depth) +{ +#if defined(__arm__) + struct frame_tail_eabi *tail; + struct frame_tail_eabi *next; + struct frame_tail_eabi *ptrtail; + struct frame_tail_eabi buftail; + unsigned long fp = regs->ARM_fp; + unsigned long lr = regs->ARM_lr; + int is_user_mode = user_mode(regs); + + if (!is_user_mode) { + return; + } + + /* entry preamble may not have executed */ + gator_add_trace(cpu, lr); + + /* check tail is valid */ + if (fp == 0) { + return; + } + + tail = (struct frame_tail_eabi *)(fp - 4); + + while (depth-- && tail && !((unsigned long) tail & 3)) { + /* Also check accessibility of one struct frame_tail beyond */ + if (!access_ok(VERIFY_READ, tail, sizeof(struct frame_tail_eabi))) + return; + if (__copy_from_user_inatomic(&buftail, tail, sizeof(struct frame_tail_eabi))) + return; + ptrtail = &buftail; + + lr = ptrtail[0].lr; + gator_add_trace(cpu, lr); + + /* frame pointers should progress back up the stack, towards higher addresses */ + next = (struct frame_tail_eabi *)(lr - 4); + if (tail >= next || lr == 0) { + fp = ptrtail[0].fp; + next = (struct frame_tail_eabi *)(fp - 4); + /* check tail is valid */ + if (tail >= next || fp == 0) { + return; + } + } + + tail = next; + } +#endif +} diff --git a/driver/gator_cookies.c b/driver/gator_cookies.c new file mode 100644 index 0000000..4e39667 --- /dev/null +++ b/driver/gator_cookies.c @@ -0,0 +1,224 @@ +/** + * Copyright (C) ARM Limited 2010-2011. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#define COOKIEMAP_ENTRIES 1024 /* must be power of 2 */ +#define MAX_COLLISIONS 2 + +static DEFINE_PER_CPU(uint32_t, cookie_next_key); +static DEFINE_PER_CPU(uint64_t *, cookie_keys); +static DEFINE_PER_CPU(uint32_t *, cookie_values); + +static uint32_t *gator_crc32_table; + +static uint32_t cookiemap_code(uint32_t value) { + uint32_t cookiecode = (value >> 24) & 0xff; + cookiecode = cookiecode * 31 + ((value >> 16) & 0xff); + cookiecode = cookiecode * 31 + ((value >> 8) & 0xff); + cookiecode = cookiecode * 31 + ((value >> 0) & 0xff); + cookiecode &= (COOKIEMAP_ENTRIES-1); + return cookiecode * MAX_COLLISIONS; +} + +static uint32_t gator_chksum_crc32(char *data) +{ + register unsigned long crc; + unsigned char *block = data; + int i, length = strlen(data); + + crc = 0xFFFFFFFF; + for (i = 0; i < length; i++) { + crc = ((crc >> 8) & 0x00FFFFFF) ^ gator_crc32_table[(crc ^ *block++) & 0xFF]; + } + + return (crc ^ 0xFFFFFFFF); +} + +/* + * Exists + * Pre: [0][1][v][3]..[n-1] + * Post: [v][0][1][3]..[n-1] + */ +static uint32_t cookiemap_exists(uint64_t key) { + int cpu = raw_smp_processor_id(); + uint32_t cookiecode = cookiemap_code(key); + uint64_t *keys = &(per_cpu(cookie_keys, cpu)[cookiecode]); + uint32_t *values = &(per_cpu(cookie_values, cpu)[cookiecode]); + int x; + + for (x = 0; x < MAX_COLLISIONS; x++) { + if (keys[x] == key) { + uint32_t value = values[x]; + for (; x > 0; x--) { + keys[x] = keys[x-1]; + values[x] = values[x-1]; + } + keys[0] = key; + values[0] = value; + return value; + } + } + + return 0; +} + +/* + * Add + * Pre: [0][1][2][3]..[n-1] + * Post: [v][0][1][2]..[n-2] + */ +static void cookiemap_add(uint64_t key, uint32_t value) { + int cpu = raw_smp_processor_id(); + int cookiecode = cookiemap_code(key); + uint64_t *keys = &(per_cpu(cookie_keys, cpu)[cookiecode]); + uint32_t *values = &(per_cpu(cookie_values, cpu)[cookiecode]); + int x; + + for (x = MAX_COLLISIONS-1; x > 0; x--) { + keys[x] = keys[x-1]; + values[x] = keys[x-1]; + } + keys[0] = key; + values[0] = value; +} + +static inline uint32_t get_cookie(int cpu, int tgid, struct vm_area_struct *vma) +{ + struct path *path; + uint64_t key; + int cookie; + char *text; + + if (!vma || !vma->vm_file) { + return INVALID_COOKIE; + } + path = &vma->vm_file->f_path; + if (!path || !path->dentry) { + return INVALID_COOKIE; + } + + text = (char*)path->dentry->d_name.name; + key = gator_chksum_crc32(text); + key = (key << 32) | (uint32_t)text; + + cookie = cookiemap_exists(key); + if (cookie) { + goto output; + } + + cookie = per_cpu(cookie_next_key, cpu)+=nr_cpu_ids; + cookiemap_add(key, cookie); + + gator_buffer_write_packed_int(cpu, PROTOCOL_COOKIE); + gator_buffer_write_packed_int(cpu, cookie); + gator_buffer_write_string(cpu, text); + +output: + return cookie; +} + +static int get_exec_cookie(int cpu, struct task_struct *task) +{ + unsigned long cookie = NO_COOKIE; + struct mm_struct *mm = task->mm; + struct vm_area_struct *vma; + + if (!mm) + return cookie; + + for (vma = mm->mmap; vma; vma = vma->vm_next) { + if (!vma->vm_file) + continue; + if (!(vma->vm_flags & VM_EXECUTABLE)) + continue; + cookie = get_cookie(cpu, task->tgid, vma); + break; + } + + return cookie; +} + +static unsigned long get_address_cookie(int cpu, struct task_struct *task, unsigned long addr, off_t *offset) +{ + unsigned long cookie = NO_COOKIE; + struct mm_struct *mm = task->mm; + struct vm_area_struct *vma; + + if (!mm) + return cookie; + + for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) { + if (addr < vma->vm_start || addr >= vma->vm_end) + continue; + + if (vma->vm_file) { + cookie = get_cookie(cpu, task->tgid, vma); + *offset = (vma->vm_pgoff << PAGE_SHIFT) + addr - vma->vm_start; + } else { + /* must be an anonymous map */ + *offset = addr; + } + + break; + } + + if (!vma) + cookie = INVALID_COOKIE; + + return cookie; +} + +static void cookies_initialize(void) +{ + uint32_t crc, poly; + int cpu, size; + int i, j; + + for_each_present_cpu(cpu) { + per_cpu(cookie_next_key, cpu) = nr_cpu_ids + cpu; + + size = COOKIEMAP_ENTRIES * MAX_COLLISIONS * sizeof(uint64_t); + per_cpu(cookie_keys, cpu) = (uint64_t*)kmalloc(size, GFP_KERNEL); + memset(per_cpu(cookie_keys, cpu), 0, size); + + size = COOKIEMAP_ENTRIES * MAX_COLLISIONS * sizeof(uint32_t); + per_cpu(cookie_values, cpu) = (uint32_t*)kmalloc(size, GFP_KERNEL); + memset(per_cpu(cookie_values, cpu), 0, size); + } + + // build CRC32 table + poly = 0x04c11db7; + gator_crc32_table = (uint32_t*)kmalloc(256 * sizeof(uint32_t), GFP_KERNEL); + for (i = 0; i < 256; i++) { + crc = i; + for (j = 8; j > 0; j--) { + if (crc & 1) { + crc = (crc >> 1) ^ poly; + } else { + crc >>= 1; + } + } + gator_crc32_table[i] = crc; + } +} + +static void cookies_release(void) +{ + int cpu; + + for_each_present_cpu(cpu) { + kfree(per_cpu(cookie_keys, cpu)); + per_cpu(cookie_keys, cpu) = NULL; + + kfree(per_cpu(cookie_values, cpu)); + per_cpu(cookie_values, cpu) = NULL; + } + + kfree(gator_crc32_table); + gator_crc32_table = NULL; +} diff --git a/driver/gator_events.c b/driver/gator_events.c new file mode 100644 index 0000000..d837257 --- /dev/null +++ b/driver/gator_events.c @@ -0,0 +1,40 @@ +/** + * Copyright (C) ARM Limited 2010-2011. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +/** + * This file is #included in gator_main.c + * Update this file and Makefile to add custom counters. + */ + +extern int gator_events_armv6_install(gator_interface *gi); +extern int gator_events_armv7_install(gator_interface *gi); +extern int gator_events_irq_install(gator_interface *gi); +extern int gator_events_sched_install(gator_interface *gi); +extern int gator_events_block_install(gator_interface *gi); +extern int gator_events_meminfo_install(gator_interface *gi); +extern int gator_events_net_install(gator_interface *gi); + +static int gator_events_install(void) +{ + if (gator_event_install(gator_events_armv6_install)) + return -1; + if (gator_event_install(gator_events_armv7_install)) + return -1; + if (gator_event_install(gator_events_irq_install)) + return -1; + if (gator_event_install(gator_events_sched_install)) + return -1; + if (gator_event_install(gator_events_block_install)) + return -1; + if (gator_event_install(gator_events_meminfo_install)) + return -1; + if (gator_event_install(gator_events_net_install)) + return -1; + return 0; +} diff --git a/driver/gator_events_armv6.c b/driver/gator_events_armv6.c new file mode 100644 index 0000000..c571e44 --- /dev/null +++ b/driver/gator_events_armv6.c @@ -0,0 +1,233 @@ +/** + * Copyright (C) ARM Limited 2010-2011. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "gator.h" + +#if defined(__arm__) + +#define ARM1136 0xb36 +#define ARM1156 0xb56 +#define ARM1176 0xb76 + +static const char *pmnc_name; + +extern u32 gator_cpuid(void); + +/* + * Per-CPU PMCR + */ +#define PMCR_E (1 << 0) /* Enable */ +#define PMCR_P (1 << 1) /* Count reset */ +#define PMCR_C (1 << 2) /* Cycle counter reset */ +#define PMCR_OFL_PMN0 (1 << 8) /* Count reg 0 overflow */ +#define PMCR_OFL_PMN1 (1 << 9) /* Count reg 1 overflow */ +#define PMCR_OFL_CCNT (1 << 10) /* Cycle counter overflow */ + +#define PMN0 0 +#define PMN1 1 +#define CCNT 2 +#define CNTMAX (CCNT+1) + +static int pmnc_count = 0; +static unsigned long pmnc_enabled[CNTMAX]; +static unsigned long pmnc_event[CNTMAX]; +static unsigned long pmnc_key[CNTMAX]; + +static DEFINE_PER_CPU(int[CNTMAX], perfPrev); +static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); + +static inline void armv6_pmnc_write(u32 val) +{ + /* upper 4bits and 7, 11 are write-as-0 */ + val &= 0x0ffff77f; + asm volatile("mcr p15, 0, %0, c15, c12, 0" : : "r" (val)); +} + +static inline u32 armv6_pmnc_read(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c15, c12, 0" : "=r" (val)); + return val; +} + +static void armv6_pmnc_reset_counter(unsigned int cnt) +{ + u32 val = 0; + switch (cnt) { + case CCNT: + asm volatile("mcr p15, 0, %0, c15, c12, 1" : : "r" (val)); + break; + case PMN0: + asm volatile("mcr p15, 0, %0, c15, c12, 2" : : "r" (val)); + break; + case PMN1: + asm volatile("mcr p15, 0, %0, c15, c12, 3" : : "r" (val)); + break; + } +} + +int gator_events_armv6_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int i; + + pmnc_count = 3; + + for (i = PMN0; i <= CCNT; i++) { + char buf[40]; + if (i == CCNT) { + snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name); + } else { + snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i); + } + dir = gatorfs_mkdir(sb, root, buf); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]); + if (i != CCNT) { + gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]); + } + gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]); + } + + return 0; +} + +static int gator_events_armv6_init(int *key) +{ + unsigned int cnt; + + for (cnt = PMN0; cnt <= CCNT; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + pmnc_key[cnt] = *key; + *key = *key + 1; + } + + return 0; +} + +static void gator_events_armv6_online(void) +{ + unsigned int cnt; + u32 pmnc; + + if (armv6_pmnc_read() & PMCR_E) { + armv6_pmnc_write(armv6_pmnc_read() & ~PMCR_E); + } + + /* initialize PMNC, reset overflow, D bit, C bit and P bit. */ + armv6_pmnc_write(PMCR_OFL_PMN0 | PMCR_OFL_PMN1 | PMCR_OFL_CCNT | + PMCR_C | PMCR_P); + + /* configure control register */ + for (pmnc = 0, cnt = PMN0; cnt <= CCNT; cnt++) { + unsigned long event; + + per_cpu(perfPrev, raw_smp_processor_id())[cnt] = 0; + + if (!pmnc_enabled[cnt]) + continue; + + event = pmnc_event[cnt] & 255; + + /* + * Set event (if destined for PMNx counters) + */ + if (cnt == PMN0) { + pmnc |= event << 20; + } else if (cnt == PMN1) { + pmnc |= event << 12; + } + + /* + * Reset counter + */ + armv6_pmnc_reset_counter(cnt); + } + armv6_pmnc_write(pmnc | PMCR_E); +} + +static void gator_events_armv6_offline(void) +{ + unsigned int cnt; + + armv6_pmnc_write(armv6_pmnc_read() & ~PMCR_E); + for (cnt = PMN0; cnt <= CCNT; cnt++) { + armv6_pmnc_reset_counter(cnt); + } +} + +static void gator_events_armv6_stop(void) +{ + unsigned int cnt; + + for (cnt = PMN0; cnt <= CCNT; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + } +} + +static int gator_events_armv6_read(int **buffer) +{ + int cnt, len = 0; + int cpu = raw_smp_processor_id(); + + for (cnt = PMN0; cnt <= CCNT; cnt++) { + if (pmnc_enabled[cnt]) { + u32 value = 0; + switch (cnt) { + case CCNT: + asm volatile("mrc p15, 0, %0, c15, c12, 1" : "=r" (value)); + break; + case PMN0: + asm volatile("mrc p15, 0, %0, c15, c12, 2" : "=r" (value)); + break; + case PMN1: + asm volatile("mrc p15, 0, %0, c15, c12, 3" : "=r" (value)); + break; + } + armv6_pmnc_reset_counter(cnt); + if (value != per_cpu(perfPrev, cpu)[cnt]) { + per_cpu(perfPrev, cpu)[cnt] = value; + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = value; + } + } + } + + // update or discard + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} +#endif + +int gator_events_armv6_install(gator_interface *gi) { +#if defined(__arm__) + switch (gator_cpuid()) { + case ARM1136: + case ARM1156: + case ARM1176: + pmnc_name = "ARM11"; + break; + default: + return -1; + } + + gi->create_files = gator_events_armv6_create_files; + gi->init = gator_events_armv6_init; + gi->stop = gator_events_armv6_stop; + gi->online = gator_events_armv6_online; + gi->offline = gator_events_armv6_offline; + gi->read = gator_events_armv6_read; +#endif + return 0; +} diff --git a/driver/gator_events_armv7.c b/driver/gator_events_armv7.c new file mode 100644 index 0000000..5a3268f --- /dev/null +++ b/driver/gator_events_armv7.c @@ -0,0 +1,411 @@ +/** + * Copyright (C) ARM Limited 2010-2011. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "gator.h" + +#if defined(__arm__) + +#define CORTEX_A8 0xc08 +#define CORTEX_A9 0xc09 + +static const char *pmnc_name; +static int pmnc_count; + +extern u32 gator_cpuid(void); + +/* + * Per-CPU PMNC: config reg + */ +#define PMNC_E (1 << 0) /* Enable all counters */ +#define PMNC_P (1 << 1) /* Reset all counters */ +#define PMNC_C (1 << 2) /* Cycle counter reset */ +#define PMNC_D (1 << 3) /* CCNT counts every 64th cpu cycle */ +#define PMNC_X (1 << 4) /* Export to ETM */ +#define PMNC_DP (1 << 5) /* Disable CCNT if non-invasive debug*/ +#define PMNC_MASK 0x3f /* Mask for writable bits */ + +/* + * CNTENS: counters enable reg + */ +#define CNTENS_P0 (1 << 0) +#define CNTENS_P1 (1 << 1) +#define CNTENS_P2 (1 << 2) +#define CNTENS_P3 (1 << 3) +#define CNTENS_C (1 << 31) +#define CNTENS_MASK 0x8000000f /* Mask for writable bits */ + +/* + * CNTENC: counters disable reg + */ +#define CNTENC_P0 (1 << 0) +#define CNTENC_P1 (1 << 1) +#define CNTENC_P2 (1 << 2) +#define CNTENC_P3 (1 << 3) +#define CNTENC_C (1 << 31) +#define CNTENC_MASK 0x8000000f /* Mask for writable bits */ + +/* + * INTENS: counters overflow interrupt enable reg + */ +#define INTENS_P0 (1 << 0) +#define INTENS_P1 (1 << 1) +#define INTENS_P2 (1 << 2) +#define INTENS_P3 (1 << 3) +#define INTENS_C (1 << 31) +#define INTENS_MASK 0x8000000f /* Mask for writable bits */ + +/* + * EVTSEL: Event selection reg + */ +#define EVTSEL_MASK 0x7f /* Mask for writable bits */ + +/* + * SELECT: Counter selection reg + */ +#define SELECT_MASK 0x1f /* Mask for writable bits */ + +/* + * FLAG: counters overflow flag status reg + */ +#define FLAG_P0 (1 << 0) +#define FLAG_P1 (1 << 1) +#define FLAG_P2 (1 << 2) +#define FLAG_P3 (1 << 3) +#define FLAG_C (1 << 31) +#define FLAG_MASK 0x8000000f /* Mask for writable bits */ + +#define CCNT 0 +#define CNT0 1 +#define CNTMAX (6+1) + +static unsigned long pmnc_enabled[CNTMAX]; +static unsigned long pmnc_event[CNTMAX]; +static unsigned long pmnc_key[CNTMAX]; + +static DEFINE_PER_CPU(int[CNTMAX], perfPrev); +static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); + +static inline void armv7_pmnc_write(u32 val) +{ + val &= PMNC_MASK; + asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val)); +} + +static inline u32 armv7_pmnc_read(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val)); + return val; +} + +static inline u32 armv7_ccnt_read(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); + return val; +} + +static inline u32 armv7_cntn_read(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val)); + return val; +} + +static inline u32 armv7_pmnc_enable_counter(unsigned int cnt) +{ + u32 val; + + if (cnt >= CNTMAX) { + pr_err("gator: CPU%u enabling wrong PMNC counter %d\n", smp_processor_id(), cnt); + return -1; + } + + if (cnt == CCNT) + val = CNTENS_C; + else + val = (1 << (cnt - CNT0)); + + val &= CNTENS_MASK; + asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val)); + + return cnt; +} + +static inline u32 armv7_pmnc_disable_counter(unsigned int cnt) +{ + u32 val; + + if (cnt >= CNTMAX) { + pr_err("gator: CPU%u disabling wrong PMNC counter %d\n", smp_processor_id(), cnt); + return -1; + } + + if (cnt == CCNT) + val = CNTENC_C; + else + val = (1 << (cnt - CNT0)); + + val &= CNTENC_MASK; + asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val)); + + return cnt; +} + +static inline u32 armv7_pmnc_enable_intens(unsigned int cnt) +{ + u32 val; + + if (cnt >= CNTMAX) { + pr_err("gator: CPU%u enabling wrong PMNC counter interrupt enable %d\n", smp_processor_id(), cnt); + return -1; + } + + if (cnt == CCNT) + val = INTENS_C; + else + val = (1 << (cnt - CNT0)); + + val &= INTENS_MASK; + asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (val)); + + return cnt; +} + +static inline u32 armv7_pmnc_getreset_flags(void) +{ + u32 val; + + /* Read */ + asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val)); + + /* Write to clear flags */ + val &= FLAG_MASK; + asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (val)); + + return val; +} + +static inline int armv7_pmnc_select_counter(unsigned int cnt) +{ + u32 val; + + if ((cnt == CCNT) || (cnt >= CNTMAX)) { + pr_err("gator: CPU%u selecting wrong PMNC counter %d\n", smp_processor_id(), cnt); + return -1; + } + + val = (cnt - CNT0) & SELECT_MASK; + asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val)); + + return cnt; +} + +static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val) +{ + if (armv7_pmnc_select_counter(cnt) == cnt) { + val &= EVTSEL_MASK; + asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val)); + } +} + +static void armv7_pmnc_reset_counter(unsigned int cnt) +{ + u32 val = 0; + + if (cnt == CCNT) { + armv7_pmnc_disable_counter(cnt); + + asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val)); + + if (pmnc_enabled[cnt] != 0) + armv7_pmnc_enable_counter(cnt); + + } else if (cnt >= CNTMAX) { + pr_err("gator: CPU%u resetting wrong PMNC counter %d\n", smp_processor_id(), cnt); + } else { + armv7_pmnc_disable_counter(cnt); + + if (armv7_pmnc_select_counter(cnt) == cnt) + asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val)); + + if (pmnc_enabled[cnt] != 0) + armv7_pmnc_enable_counter(cnt); + } +} + +static int gator_events_armv7_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int i; + + for (i = 0; i < pmnc_count; i++) { + char buf[40]; + if (i == 0) { + snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name); + } else { + snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i-1); + } + dir = gatorfs_mkdir(sb, root, buf); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]); + if (i > 0) { + gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]); + } + gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]); + } + + return 0; +} + +static int gator_events_armv7_init(int *key) +{ + unsigned int cnt; + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + pmnc_key[cnt] = *key; + *key = *key + 1; + } + + return 0; +} + +static void gator_events_armv7_online(void) +{ + unsigned int cnt; + + if (armv7_pmnc_read() & PMNC_E) { + armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E); + } + + /* Initialize & Reset PMNC: C bit and P bit */ + armv7_pmnc_write(PMNC_P | PMNC_C); + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + unsigned long event; + + per_cpu(perfPrev, raw_smp_processor_id())[cnt] = 0; + + if (!pmnc_enabled[cnt]) + continue; + + /* + * Disable counter + */ + armv7_pmnc_disable_counter(cnt); + + event = pmnc_event[cnt] & 255; + + /* + * Set event (if destined for PMNx counters) + * We don't need to set the event if it's a cycle count + */ + if (cnt != CCNT) + armv7_pmnc_write_evtsel(cnt, event); + + /* + * [Do not] Enable interrupt for this counter + */ + /* armv7_pmnc_enable_intens(cnt); */ + + /* + * Reset counter + */ + armv7_pmnc_reset_counter(cnt); + + /* + * Enable counter + */ + armv7_pmnc_enable_counter(cnt); + } + + // enable + armv7_pmnc_write(armv7_pmnc_read() | PMNC_E); +} + +static void gator_events_armv7_offline(void) +{ + armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E); +} + +static void gator_events_armv7_stop(void) +{ + unsigned int cnt; + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + } +} + +static int gator_events_armv7_read(int **buffer) +{ + int cnt, len = 0; + int cpu = raw_smp_processor_id(); + + if (!pmnc_count) + return 0; + + armv7_pmnc_getreset_flags(); + for (cnt = 0; cnt < pmnc_count; cnt++) { + if (pmnc_enabled[cnt]) { + int value; + if (cnt == CCNT) { + value = armv7_ccnt_read(); + } else if (armv7_pmnc_select_counter(cnt) == cnt) { + value = armv7_cntn_read(); + } else { + value = 0; + } + armv7_pmnc_reset_counter(cnt); + if (value != per_cpu(perfPrev, cpu)[cnt]) { + per_cpu(perfPrev, cpu)[cnt] = value; + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = value; + } + } + } + + // update or discard + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} +#endif + +int gator_events_armv7_install(gator_interface *gi) { +#if defined(__arm__) + switch (gator_cpuid()) { + case CORTEX_A8: + pmnc_name = "Cortex-A8"; + pmnc_count = 4; + break; + case CORTEX_A9: + pmnc_name = "Cortex-A9"; + pmnc_count = 6; + break; + default: + return -1; + } + + pmnc_count++; // CNT[n] + CCNT + + gi->create_files = gator_events_armv7_create_files; + gi->init = gator_events_armv7_init; + gi->stop = gator_events_armv7_stop; + gi->online = gator_events_armv7_online; + gi->offline = gator_events_armv7_offline; + gi->read = gator_events_armv7_read; +#endif + return 0; +} diff --git a/driver/gator_events_block.c b/driver/gator_events_block.c new file mode 100644 index 0000000..f32d8ef --- /dev/null +++ b/driver/gator_events_block.c @@ -0,0 +1,174 @@ +/** + * Copyright (C) ARM Limited 2010-2011. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include "gator.h" +#include + +#define BLOCK_RQ_WR 0 +#define BLOCK_RQ_RD 1 + +#define BLOCK_TOTAL (BLOCK_RQ_RD+1) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +#define EVENTWRITE REQ_RW +#else +#define EVENTWRITE REQ_WRITE +#endif + +static ulong block_rq_wr_enabled; +static ulong block_rq_rd_enabled; +static ulong block_rq_wr_key; +static ulong block_rq_rd_key; +static DEFINE_PER_CPU(int[BLOCK_TOTAL], blockCnt); +static DEFINE_PER_CPU(int[BLOCK_TOTAL * 2], blockGet); +static DEFINE_PER_CPU(bool, new_data_avail); + +GATOR_DEFINE_PROBE(block_rq_complete, TP_PROTO(struct request_queue *q, struct request *rq)) +{ + unsigned long flags; + int write, size; + int cpu = raw_smp_processor_id(); + + if (!rq) + return; + + write = rq->cmd_flags & EVENTWRITE; + size = rq->resid_len; + + if (!size) + return; + + // disable interrupts to synchronize with gator_events_block_read() + // spinlocks not needed since percpu buffers are used + local_irq_save(flags); + if (write) + per_cpu(blockCnt, cpu)[BLOCK_RQ_WR] += size; + else + per_cpu(blockCnt, cpu)[BLOCK_RQ_RD] += size; + local_irq_restore(flags); + + per_cpu(new_data_avail, cpu) = true; +} + +static int gator_events_block_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + /* block_complete_wr */ + dir = gatorfs_mkdir(sb, root, "Linux_block_rq_wr"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &block_rq_wr_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &block_rq_wr_key); + + /* block_complete_rd */ + dir = gatorfs_mkdir(sb, root, "Linux_block_rq_rd"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &block_rq_rd_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &block_rq_rd_key); + + return 0; +} + +static int gator_events_block_init(int *key) +{ + block_rq_wr_enabled = 0; + block_rq_rd_enabled = 0; + + block_rq_wr_key = *key; + *key = *key + 1; + block_rq_rd_key = *key; + *key = *key + 1; + + return 0; +} + +static int gator_events_block_start(void) +{ + int cpu; + + for_each_present_cpu(cpu) + per_cpu(new_data_avail, cpu) = true; + + // register tracepoints + if (block_rq_wr_enabled || block_rq_rd_enabled) + if (GATOR_REGISTER_TRACE(block_rq_complete)) + goto fail_block_rq_exit; + pr_debug("gator: registered block event tracepoints\n"); + + return 0; + + // unregister tracepoints on error +fail_block_rq_exit: + pr_err("gator: block event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +static void gator_events_block_stop(void) +{ + if (block_rq_wr_enabled || block_rq_rd_enabled) + GATOR_UNREGISTER_TRACE(block_rq_complete); + pr_debug("gator: unregistered block event tracepoints\n"); + + block_rq_wr_enabled = 0; + block_rq_rd_enabled = 0; +} + +static int gator_events_block_read(int **buffer) +{ + unsigned long flags; + int len, value, cpu, data = 0; + cpu = raw_smp_processor_id(); + + if (per_cpu(new_data_avail, cpu) == false) + return 0; + + per_cpu(new_data_avail, cpu) = false; + + len = 0; + if (block_rq_wr_enabled) { + local_irq_save(flags); + value = per_cpu(blockCnt, cpu)[BLOCK_RQ_WR]; + per_cpu(blockCnt, cpu)[BLOCK_RQ_WR] = 0; + local_irq_restore(flags); + per_cpu(blockGet, cpu)[len++] = block_rq_wr_key; + per_cpu(blockGet, cpu)[len++] = value; + data += value; + } + if (block_rq_rd_enabled) { + local_irq_save(flags); + value = per_cpu(blockCnt, cpu)[BLOCK_RQ_RD]; + per_cpu(blockCnt, cpu)[BLOCK_RQ_RD] = 0; + local_irq_restore(flags); + per_cpu(blockGet, cpu)[len++] = block_rq_rd_key; + per_cpu(blockGet, cpu)[len++] = value; + data += value; + } + + if (data != 0) + per_cpu(new_data_avail, cpu) = true; + + if (buffer) + *buffer = per_cpu(blockGet, cpu); + + return len; +} + +int gator_events_block_install(gator_interface *gi) { + gi->create_files = gator_events_block_create_files; + gi->init = gator_events_block_init; + gi->start = gator_events_block_start; + gi->stop = gator_events_block_stop; + gi->read = gator_events_block_read; + return 0; +} diff --git a/driver/gator_events_irq.c b/driver/gator_events_irq.c new file mode 100644 index 0000000..7bbbd4b --- /dev/null +++ b/driver/gator_events_irq.c @@ -0,0 +1,174 @@ +/** + * Copyright (C) ARM Limited 2010-2011. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include "gator.h" +#include + +#define HARDIRQ 0 +#define SOFTIRQ 1 +#define TOTALIRQ (SOFTIRQ+1) + +static ulong hardirq_enabled; +static ulong softirq_enabled; +static ulong hardirq_key; +static ulong softirq_key; +static DEFINE_PER_CPU(int[TOTALIRQ], irqCnt); +static DEFINE_PER_CPU(int[TOTALIRQ], irqPrev); +static DEFINE_PER_CPU(int[TOTALIRQ * 2], irqGet); + +GATOR_DEFINE_PROBE(irq_handler_exit, TP_PROTO(int irq, + struct irqaction *action, int ret)) +{ + unsigned long flags; + + // disable interrupts to synchronize with gator_events_irq_read() + // spinlocks not needed since percpu buffers are used + local_irq_save(flags); + per_cpu(irqCnt, raw_smp_processor_id())[HARDIRQ]++; + local_irq_restore(flags); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) +GATOR_DEFINE_PROBE(softirq_exit, TP_PROTO(struct softirq_action *h, struct softirq_action *vec)) +#else +GATOR_DEFINE_PROBE(softirq_exit, TP_PROTO(unsigned int vec_nr)) +#endif +{ + unsigned long flags; + + // disable interrupts to synchronize with gator_events_irq_read() + // spinlocks not needed since percpu buffers are used + local_irq_save(flags); + per_cpu(irqCnt, raw_smp_processor_id())[SOFTIRQ]++; + local_irq_restore(flags); +} + +static int gator_events_irq_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + /* irq */ + dir = gatorfs_mkdir(sb, root, "Linux_irq_irq"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &hardirq_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &hardirq_key); + + /* soft irq */ + dir = gatorfs_mkdir(sb, root, "Linux_irq_softirq"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &softirq_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &softirq_key); + + return 0; +} + +static int gator_events_irq_init(int *key) +{ + hardirq_key = *key; + *key = *key + 1; + softirq_key = *key; + *key = *key + 1; + + hardirq_enabled = 0; + softirq_enabled = 0; + + return 0; +} + +static int gator_events_irq_start(void) +{ + int cpu, i; + + for_each_present_cpu(cpu) { + for (i = 0; i < TOTALIRQ; i++) + per_cpu(irqPrev, cpu)[i] = 0; + } + + // register tracepoints + if (hardirq_enabled) + if (GATOR_REGISTER_TRACE(irq_handler_exit)) + goto fail_hardirq_exit; + if (softirq_enabled) + if (GATOR_REGISTER_TRACE(softirq_exit)) + goto fail_softirq_exit; + pr_debug("gator: registered irq tracepoints\n"); + + return 0; + + // unregister tracepoints on error +fail_softirq_exit: + if (hardirq_enabled) + GATOR_UNREGISTER_TRACE(irq_handler_exit); +fail_hardirq_exit: + pr_err("gator: irq tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +static void gator_events_irq_stop(void) +{ + if (hardirq_enabled) + GATOR_UNREGISTER_TRACE(irq_handler_exit); + if (softirq_enabled) + GATOR_UNREGISTER_TRACE(softirq_exit); + pr_debug("gator: unregistered irq tracepoints\n"); + + hardirq_enabled = 0; + softirq_enabled = 0; +} + +static int gator_events_irq_read(int **buffer) +{ + unsigned long flags; + int len, value; + int cpu = raw_smp_processor_id(); + + len = 0; + if (hardirq_enabled) { + local_irq_save(flags); + value = per_cpu(irqCnt, cpu)[HARDIRQ]; + per_cpu(irqCnt, cpu)[HARDIRQ] = 0; + local_irq_restore(flags); + if (value != per_cpu(irqPrev, cpu)[HARDIRQ]) { + per_cpu(irqPrev, cpu)[HARDIRQ] = value; + per_cpu(irqGet, cpu)[len++] = hardirq_key; + per_cpu(irqGet, cpu)[len++] = value; + } + } + + if (softirq_enabled) { + local_irq_save(flags); + value = per_cpu(irqCnt, cpu)[SOFTIRQ]; + per_cpu(irqCnt, cpu)[SOFTIRQ] = 0; + local_irq_restore(flags); + if (value != per_cpu(irqPrev, cpu)[SOFTIRQ]) { + per_cpu(irqPrev, cpu)[SOFTIRQ] = value; + per_cpu(irqGet, cpu)[len++] = softirq_key; + per_cpu(irqGet, cpu)[len++] = value; + } + } + + if (buffer) + *buffer = per_cpu(irqGet, cpu); + + return len; +} + +int gator_events_irq_install(gator_interface *gi) { + gi->create_files = gator_events_irq_create_files; + gi->init = gator_events_irq_init; + gi->start = gator_events_irq_start; + gi->stop = gator_events_irq_stop; + gi->read = gator_events_irq_read; + return 0; +} diff --git a/driver/gator_events_meminfo.c b/driver/gator_events_meminfo.c new file mode 100644 index 0000000..1b576f8 --- /dev/null +++ b/driver/gator_events_meminfo.c @@ -0,0 +1,197 @@ +/** + * Copyright (C) ARM Limited 2010-2011. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include "gator.h" +#include +#include + +#define MEMINFO_MEMFREE 0 +#define MEMINFO_MEMUSED 1 +#define MEMINFO_BUFFERRAM 2 +#define MEMINFO_TOTAL 3 + +static ulong meminfo_global_enabled; +static ulong meminfo_enabled[MEMINFO_TOTAL]; +static ulong meminfo_key[MEMINFO_TOTAL]; +static int meminfo_buffer[MEMINFO_TOTAL * 2]; +static int meminfo_length = 0; +static unsigned int mem_event = 0; +static bool new_data_avail; + +static void wq_sched_handler(struct work_struct *wsptr); + +DECLARE_WORK(work, wq_sched_handler); + +GATOR_DEFINE_PROBE(mm_page_free_direct, TP_PROTO(struct page *page, unsigned int order)) { + mem_event++; +} + +GATOR_DEFINE_PROBE(mm_pagevec_free, TP_PROTO(struct page *page, int cold)) { + mem_event++; +} + +GATOR_DEFINE_PROBE(mm_page_alloc, TP_PROTO(struct page *page, unsigned int order, gfp_t gfp_flags, int migratetype)) { + mem_event++; +} + +static int gator_events_meminfo_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int i; + + for (i = 0; i < MEMINFO_TOTAL; i++) { + switch (i) { + case MEMINFO_MEMFREE: + dir = gatorfs_mkdir(sb, root, "Linux_meminfo_memfree"); + break; + case MEMINFO_MEMUSED: + dir = gatorfs_mkdir(sb, root, "Linux_meminfo_memused"); + break; + case MEMINFO_BUFFERRAM: + dir = gatorfs_mkdir(sb, root, "Linux_meminfo_bufferram"); + break; + default: + return -1; + } + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &meminfo_enabled[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &meminfo_key[i]); + } + + return 0; +} + +static int gator_events_meminfo_init(int *key) +{ + int i; + + meminfo_global_enabled = 0; + for (i = 0; i < MEMINFO_TOTAL; i++) { + meminfo_enabled[i] = 0; + meminfo_key[i] = *key; + *key = *key + 1; + } + + return 0; +} + +static int gator_events_meminfo_start(void) +{ + int i; + + new_data_avail = true; + for (i = 0; i < MEMINFO_TOTAL; i++) { + if (meminfo_enabled[i]) { + meminfo_global_enabled = 1; + } + } + + if (meminfo_global_enabled == 0) + return 0; + + if (GATOR_REGISTER_TRACE(mm_page_free_direct)) + goto mm_page_free_direct_exit; + if (GATOR_REGISTER_TRACE(mm_pagevec_free)) + goto mm_pagevec_free_exit; + if (GATOR_REGISTER_TRACE(mm_page_alloc)) + goto mm_page_alloc_exit; + + return 0; + +mm_page_alloc_exit: + GATOR_UNREGISTER_TRACE(mm_pagevec_free); +mm_pagevec_free_exit: + GATOR_UNREGISTER_TRACE(mm_page_free_direct); +mm_page_free_direct_exit: + return -1; +} + +static void gator_events_meminfo_stop(void) +{ + int i; + + if (meminfo_global_enabled) { + GATOR_UNREGISTER_TRACE(mm_page_free_direct); + GATOR_UNREGISTER_TRACE(mm_pagevec_free); + GATOR_UNREGISTER_TRACE(mm_page_alloc); + } + + meminfo_global_enabled = 0; + for (i = 0; i < MEMINFO_TOTAL; i++) { + meminfo_enabled[i] = 0; + } +} + +// Must be run in a work queue as the kernel function si_meminfo() can sleep +static void wq_sched_handler(struct work_struct *wsptr) +{ + struct sysinfo info; + int i, len, value; + + meminfo_length = len = 0; + + si_meminfo(&info); + for (i = 0; i < MEMINFO_TOTAL; i++) { + if (meminfo_enabled[i]) { + switch (i) { + case MEMINFO_MEMFREE: + value = info.freeram * PAGE_SIZE; + break; + case MEMINFO_MEMUSED: + value = (info.totalram - info.freeram) * PAGE_SIZE; + break; + case MEMINFO_BUFFERRAM: + value = info.bufferram * PAGE_SIZE; + break; + default: + value = 0; + break; + } + meminfo_buffer[len++] = meminfo_key[i]; + meminfo_buffer[len++] = value; + } + } + + meminfo_length = len; + new_data_avail = true; +} + +static int gator_events_meminfo_read(int **buffer) +{ + static unsigned int last_mem_event = 0; + + if (smp_processor_id() || !meminfo_global_enabled) + return 0; + + if (last_mem_event != mem_event) { + last_mem_event = mem_event; + schedule_work(&work); + } + + if (!new_data_avail) + return 0; + + new_data_avail = false; + + if (buffer) + *buffer = meminfo_buffer; + + return meminfo_length; +} + +int gator_events_meminfo_install(gator_interface *gi) { + gi->create_files = gator_events_meminfo_create_files; + gi->init = gator_events_meminfo_init; + gi->start = gator_events_meminfo_start; + gi->stop = gator_events_meminfo_stop; + gi->read = gator_events_meminfo_read; + return 0; +} diff --git a/driver/gator_events_net.c b/driver/gator_events_net.c new file mode 100644 index 0000000..3ca5911 --- /dev/null +++ b/driver/gator_events_net.c @@ -0,0 +1,176 @@ +/** + * Copyright (C) ARM Limited 2010-2011. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include "gator.h" +#include + +#define NETRX 0 +#define NETTX 1 +#define NETDRV 2 +#define TOTALNET (NETDRV+1) + +static ulong netdrv_enabled; +static ulong netrx_enabled; +static ulong nettx_enabled; +static ulong netdrv_key; +static ulong netrx_key; +static ulong nettx_key; +static int rx_total, tx_total; +static ulong netPrev[TOTALNET]; +static int netGet[TOTALNET * 2]; + +static void get_network_stats(struct work_struct *wsptr) { + int rx = 0, tx = 0; + struct net_device *dev; + + for_each_netdev(&init_net, dev) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + const struct net_device_stats *stats = dev_get_stats(dev); +#else + struct rtnl_link_stats64 temp; + const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp); +#endif + rx += stats->rx_bytes; + tx += stats->tx_bytes; + } + rx_total = rx; + tx_total = tx; +} +DECLARE_WORK(wq_get_stats, get_network_stats); + +static void calculate_delta(int *drv, int *rx, int *tx) +{ + int drv_calc, rx_calc, tx_calc; + + drv_calc = gator_net_traffic - netPrev[NETDRV]; + if (drv_calc > 0) { + netPrev[NETDRV] += drv_calc; + netPrev[NETTX] += drv_calc; + // remove tcp/ip header overhead + // approximation based on empirical measurement + netPrev[NETRX] += drv_calc / 42; + netPrev[NETTX] += drv_calc / 18; + } + + rx_calc = (int)(rx_total - netPrev[NETRX]); + if (rx_calc < 0) + rx_calc = 0; + netPrev[NETRX] += rx_calc; + + tx_calc = (int)(tx_total - netPrev[NETTX]); + if (tx_calc < 0) + tx_calc = 0; + netPrev[NETTX] += tx_calc; + + *drv = drv_calc; + *rx = rx_calc; + *tx = tx_calc; +} + +static int gator_events_net_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + dir = gatorfs_mkdir(sb, root, "Linux_net_drv"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &netdrv_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &netdrv_key); + + dir = gatorfs_mkdir(sb, root, "Linux_net_rx"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &netrx_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &netrx_key); + + dir = gatorfs_mkdir(sb, root, "Linux_net_tx"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &nettx_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &nettx_key); + + return 0; +} + +static int gator_events_net_init(int *key) +{ + netdrv_key = *key; + *key = *key + 1; + netrx_key = *key; + *key = *key + 1; + nettx_key = *key; + *key = *key + 1; + + netdrv_enabled = 0; + netrx_enabled = 0; + nettx_enabled = 0; + + return 0; +} + +static int gator_events_net_start(void) +{ + get_network_stats(NULL); + netPrev[NETDRV] = 0; + netPrev[NETRX] = rx_total; + netPrev[NETTX] = tx_total; + return 0; +} + +static void gator_events_net_stop(void) +{ + netdrv_enabled = 0; + netrx_enabled = 0; + nettx_enabled = 0; +} + +static int gator_events_net_read(int **buffer) +{ + int len, drv_delta, rx_delta, tx_delta; + + if (raw_smp_processor_id() != 0) + return 0; + + schedule_work(&wq_get_stats); + calculate_delta(&drv_delta, &rx_delta, &tx_delta); + + len = 0; + if (netdrv_enabled) { + netGet[len++] = netdrv_key; + netGet[len++] = drv_delta; + } + + if (netrx_enabled) { + netGet[len++] = netrx_key; + netGet[len++] = rx_delta; + } + + if (nettx_enabled) { + netGet[len++] = nettx_key; + netGet[len++] = tx_delta; + } + + if (buffer) + *buffer = netGet; + + return len; +} + +int gator_events_net_install(gator_interface *gi) { + gi->create_files = gator_events_net_create_files; + gi->init = gator_events_net_init; + gi->start = gator_events_net_start; + gi->stop = gator_events_net_stop; + gi->read = gator_events_net_read; + gator_net_traffic++; + return 0; +} diff --git a/driver/gator_events_sched.c b/driver/gator_events_sched.c new file mode 100644 index 0000000..138b7e4 --- /dev/null +++ b/driver/gator_events_sched.c @@ -0,0 +1,116 @@ +/** + * Copyright (C) ARM Limited 2010-2011. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include "gator.h" +#include + +#define SCHED_SWITCH 0 +#define SCHED_TOTAL (SCHED_SWITCH+1) + +static ulong sched_switch_enabled; +static ulong sched_switch_key; +static DEFINE_PER_CPU(int[SCHED_TOTAL], schedCnt); +static DEFINE_PER_CPU(int[SCHED_TOTAL * 2], schedGet); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next)) +#else +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next)) +#endif +{ + unsigned long flags; + + // disable interrupts to synchronize with gator_events_sched_read() + // spinlocks not needed since percpu buffers are used + local_irq_save(flags); + per_cpu(schedCnt, raw_smp_processor_id())[SCHED_SWITCH]++; + local_irq_restore(flags); +} + +static int gator_events_sched_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + /* switch */ + dir = gatorfs_mkdir(sb, root, "Linux_sched_switch"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &sched_switch_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &sched_switch_key); + + return 0; +} + +static int gator_events_sched_init(int *key) +{ + sched_switch_enabled = 0; + + sched_switch_key = *key; + *key = *key + 1; + + return 0; +} + +static int gator_events_sched_start(void) +{ + // register tracepoints + if (sched_switch_enabled) + if (GATOR_REGISTER_TRACE(sched_switch)) + goto sched_switch_exit; + pr_debug("gator: registered scheduler event tracepoints\n"); + + return 0; + + // unregister tracepoints on error +sched_switch_exit: + pr_err("gator: scheduler event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +static void gator_events_sched_stop(void) +{ + if (sched_switch_enabled) + GATOR_UNREGISTER_TRACE(sched_switch); + pr_debug("gator: unregistered scheduler event tracepoints\n"); + + sched_switch_enabled = 0; +} + +static int gator_events_sched_read(int **buffer) +{ + unsigned long flags; + int len, value; + int cpu = raw_smp_processor_id(); + + len = 0; + if (sched_switch_enabled) { + local_irq_save(flags); + value = per_cpu(schedCnt, cpu)[SCHED_SWITCH]; + per_cpu(schedCnt, cpu)[SCHED_SWITCH] = 0; + local_irq_restore(flags); + per_cpu(schedGet, cpu)[len++] = sched_switch_key; + per_cpu(schedGet, cpu)[len++] = value; + } + + if (buffer) + *buffer = per_cpu(schedGet, cpu); + + return len; +} + +int gator_events_sched_install(gator_interface *gi) { + gi->create_files = gator_events_sched_create_files; + gi->init = gator_events_sched_init; + gi->start = gator_events_sched_start; + gi->stop = gator_events_sched_stop; + gi->read = gator_events_sched_read; + return 0; +} diff --git a/driver/gator_fs.c b/driver/gator_fs.c new file mode 100644 index 0000000..5f8983f --- /dev/null +++ b/driver/gator_fs.c @@ -0,0 +1,270 @@ +/** + * @file gatorfs.c + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + * + * A simple filesystem for configuration and + * access of oprofile. + */ + +#include +#include +#include +#include +#include + +#define gatorfs_MAGIC 0x24051020 +#define TMPBUFSIZE 50 +DEFINE_SPINLOCK(gatorfs_lock); + +void gator_op_create_files(struct super_block *sb, struct dentry *root); + +static struct inode *gatorfs_get_inode(struct super_block *sb, int mode) +{ + struct inode *inode = new_inode(sb); + + if (inode) { + inode->i_mode = mode; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + } + return inode; +} + +static const struct super_operations s_ops = { + .statfs = simple_statfs, + .drop_inode = generic_delete_inode, +}; + +ssize_t gatorfs_str_to_user(char const *str, char __user *buf, size_t count, loff_t *offset) +{ + return simple_read_from_buffer(buf, count, offset, str, strlen(str)); +} + +ssize_t gatorfs_ulong_to_user(unsigned long val, char __user *buf, size_t count, loff_t *offset) +{ + char tmpbuf[TMPBUFSIZE]; + size_t maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%lu\n", val); + if (maxlen > TMPBUFSIZE) + maxlen = TMPBUFSIZE; + return simple_read_from_buffer(buf, count, offset, tmpbuf, maxlen); +} + +int gatorfs_ulong_from_user(unsigned long *val, char const __user *buf, size_t count) +{ + char tmpbuf[TMPBUFSIZE]; + unsigned long flags; + + if (!count) + return 0; + + if (count > TMPBUFSIZE - 1) + return -EINVAL; + + memset(tmpbuf, 0x0, TMPBUFSIZE); + + if (copy_from_user(tmpbuf, buf, count)) + return -EFAULT; + + spin_lock_irqsave(&gatorfs_lock, flags); + *val = simple_strtoul(tmpbuf, NULL, 0); + spin_unlock_irqrestore(&gatorfs_lock, flags); + return 0; +} + +static ssize_t ulong_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + unsigned long *val = file->private_data; + return gatorfs_ulong_to_user(*val, buf, count, offset); +} + +static ssize_t ulong_write_file(struct file *file, char const __user *buf, size_t count, loff_t *offset) +{ + unsigned long *value = file->private_data; + int retval; + + if (*offset) + return -EINVAL; + + retval = gatorfs_ulong_from_user(value, buf, count); + + if (retval) + return retval; + return count; +} + +static int default_open(struct inode *inode, struct file *filp) +{ + if (inode->i_private) + filp->private_data = inode->i_private; + return 0; +} + +static const struct file_operations ulong_fops = { + .read = ulong_read_file, + .write = ulong_write_file, + .open = default_open, +}; + +static const struct file_operations ulong_ro_fops = { + .read = ulong_read_file, + .open = default_open, +}; + +static struct dentry *__gatorfs_create_file(struct super_block *sb, + struct dentry *root, char const *name, const struct file_operations *fops, + int perm) +{ + struct dentry *dentry; + struct inode *inode; + + dentry = d_alloc_name(root, name); + if (!dentry) + return NULL; + inode = gatorfs_get_inode(sb, S_IFREG | perm); + if (!inode) { + dput(dentry); + return NULL; + } + inode->i_fop = fops; + d_add(dentry, inode); + return dentry; +} + +int gatorfs_create_ulong(struct super_block *sb, struct dentry *root, + char const *name, unsigned long *val) +{ + struct dentry *d = __gatorfs_create_file(sb, root, name, + &ulong_fops, 0644); + if (!d) + return -EFAULT; + + d->d_inode->i_private = val; + return 0; +} + +int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root, + char const *name, unsigned long *val) +{ + struct dentry *d = __gatorfs_create_file(sb, root, name, + &ulong_ro_fops, 0444); + if (!d) + return -EFAULT; + + d->d_inode->i_private = val; + return 0; +} + +static ssize_t atomic_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + atomic_t *val = file->private_data; + return gatorfs_ulong_to_user(atomic_read(val), buf, count, offset); +} + +static const struct file_operations atomic_ro_fops = { + .read = atomic_read_file, + .open = default_open, +}; + +int gatorfs_create_ro_atomic(struct super_block *sb, struct dentry *root, + char const *name, atomic_t *val) +{ + struct dentry *d = __gatorfs_create_file(sb, root, name, + &atomic_ro_fops, 0444); + if (!d) + return -EFAULT; + + d->d_inode->i_private = val; + return 0; +} + +int gatorfs_create_file(struct super_block *sb, struct dentry *root, + char const *name, const struct file_operations *fops) +{ + if (!__gatorfs_create_file(sb, root, name, fops, 0644)) + return -EFAULT; + return 0; +} + +int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root, + char const *name, const struct file_operations *fops, int perm) +{ + if (!__gatorfs_create_file(sb, root, name, fops, perm)) + return -EFAULT; + return 0; +} + +struct dentry *gatorfs_mkdir(struct super_block *sb, + struct dentry *root, char const *name) +{ + struct dentry *dentry; + struct inode *inode; + + dentry = d_alloc_name(root, name); + if (!dentry) + return NULL; + inode = gatorfs_get_inode(sb, S_IFDIR | 0755); + if (!inode) { + dput(dentry); + return NULL; + } + inode->i_op = &simple_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + d_add(dentry, inode); + return dentry; +} + +static int gatorfs_fill_super(struct super_block *sb, void *data, int silent) +{ + struct inode *root_inode; + struct dentry *root_dentry; + + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = gatorfs_MAGIC; + sb->s_op = &s_ops; + sb->s_time_gran = 1; + + root_inode = gatorfs_get_inode(sb, S_IFDIR | 0755); + if (!root_inode) + return -ENOMEM; + root_inode->i_op = &simple_dir_inode_operations; + root_inode->i_fop = &simple_dir_operations; + root_dentry = d_alloc_root(root_inode); + if (!root_dentry) { + iput(root_inode); + return -ENOMEM; + } + + sb->s_root = root_dentry; + + gator_op_create_files(sb, root_dentry); + + // FIXME: verify kill_litter_super removes our dentries + return 0; +} + +static int gatorfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) +{ + return get_sb_single(fs_type, flags, data, gatorfs_fill_super, mnt); +} + +static struct file_system_type gatorfs_type = { + .owner = THIS_MODULE, + .name = "gatorfs", + .get_sb = gatorfs_get_sb, + .kill_sb = kill_litter_super, +}; + +int __init gatorfs_register(void) +{ + return register_filesystem(&gatorfs_type); +} + +void gatorfs_unregister(void) +{ + unregister_filesystem(&gatorfs_type); +} diff --git a/driver/gator_main.c b/driver/gator_main.c new file mode 100644 index 0000000..60b09cb --- /dev/null +++ b/driver/gator_main.c @@ -0,0 +1,1071 @@ +/** + * Copyright (C) ARM Limited 2010-2011. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +static unsigned long gator_protocol_version = 3; + +#include "gator.h" +#include +#include +#include +#include +#include +#include + +#ifndef CONFIG_GENERIC_TRACER +#ifndef CONFIG_TRACING +#warning gator requires the kernel to have CONFIG_GENERIC_TRACER or CONFIG_TRACING defined +#endif +#endif + +#ifndef CONFIG_PROFILING +#warning gator requires the kernel to have CONFIG_PROFILING defined +#endif + +#ifndef CONFIG_HIGH_RES_TIMERS +#warning gator requires the kernel to have CONFIG_HIGH_RES_TIMERS defined +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) +#error kernels prior to 2.6.32 are not supported +#endif + +/****************************************************************************** + * DEFINES + ******************************************************************************/ +#define BUFFER_SIZE_DEFAULT 262144 +#define SYNC_FREQ_DEFAULT 1000 + +#define NO_COOKIE 0UL +#define INVALID_COOKIE ~0UL + +#define PROTOCOL_FRAME ~0 +#define PROTOCOL_START_TICK 1 +#define PROTOCOL_END_TICK 3 +#define PROTOCOL_START_BACKTRACE 5 +#define PROTOCOL_END_BACKTRACE 7 +#define PROTOCOL_COOKIE 9 +#define PROTOCOL_SCHEDULER_TRACE 11 +#define PROTOCOL_COUNTERS 13 +#define PROTOCOL_ANNOTATE 15 +#define PROTOCOL_CPU_SYNC 17 + +#if defined(__arm__) +#define PC_REG regs->ARM_pc +#else +#define PC_REG regs->ip +#endif + +/****************************************************************************** + * PER CPU + ******************************************************************************/ +static unsigned long gator_cpu_cores; +static unsigned long gator_buffer_size; +static unsigned long gator_backtrace_depth; + +static unsigned long gator_started; +static unsigned long gator_buffer_opened; +static unsigned long gator_timer_count; +static unsigned long gator_sync_freq; +static int gator_master_tick; +static DEFINE_MUTEX(start_mutex); +static DEFINE_MUTEX(gator_buffer_mutex); + +unsigned long gator_net_traffic; + +#define COMMIT_SIZE 128 +#define COMMIT_MASK (COMMIT_SIZE-1) +static DEFINE_SPINLOCK(gator_commit_lock); +static int *gator_commit; +static int gator_commit_read; +static int gator_commit_write; + +static DECLARE_WAIT_QUEUE_HEAD(gator_buffer_wait); +static DEFINE_PER_CPU(int, gator_cpu_sync); +static DEFINE_PER_CPU(int, gator_cpu_tick); +static DEFINE_PER_CPU(int, gator_first_time); + +/****************************************************************************** + * Prototypes + ******************************************************************************/ +static void gator_buffer_write_packed_int(int cpu, unsigned int x); +static void gator_buffer_write_string(int cpu, char *x); +static void gator_add_trace(int cpu, unsigned int address); +static uint64_t gator_get_time(void); + +/****************************************************************************** + * Application Includes + ******************************************************************************/ +#include "gator_cookies.c" +#include "gator_trace_sched.c" +#include "gator_backtrace.c" +#include "gator_annotate.c" +#include "gator_events.c" +#include "gator_fs.c" + +/****************************************************************************** + * Misc + ******************************************************************************/ +#if defined(__arm__) +u32 gator_cpuid(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c0, c0, 0" : "=r" (val)); + return (val >> 4) & 0xfff; +} +#endif + +/****************************************************************************** + * Commit interface + ******************************************************************************/ +static int buffer_commit_ready(void) +{ + return (gator_commit_read != gator_commit_write); +} + +static void buffer_commit_read(int *cpu, int *readval, int *writeval) +{ + int read = gator_commit_read; + *cpu = gator_commit[read+0]; + *readval = gator_commit[read+1]; + *writeval = gator_commit[read+2]; + gator_commit_read = (read + 4) & COMMIT_MASK; +} + +static void buffer_commit_write(int cpu, int readval, int writeval) { + int write = gator_commit_write; + gator_commit[write+0] = cpu; + gator_commit[write+1] = readval; + gator_commit[write+2] = writeval; + gator_commit_write = (write + 4) & COMMIT_MASK; +} + +/****************************************************************************** + * Buffer management + ******************************************************************************/ +static uint32_t use_buffer_size; +static uint32_t use_buffer_mask; +static DEFINE_PER_CPU(int, use_buffer_seq); +static DEFINE_PER_CPU(int, use_buffer_read); +static DEFINE_PER_CPU(int, use_buffer_write); +static DEFINE_PER_CPU(char *, use_buffer); + +static void gator_buffer_write_packed_int(int cpu, unsigned int x) +{ + uint32_t write = per_cpu(use_buffer_write, cpu); + uint32_t mask = use_buffer_mask; + char *buffer = per_cpu(use_buffer, cpu); + int write0 = (write + 0) & mask; + int write1 = (write + 1) & mask; + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + int write4 = (write + 4) & mask; + int write5 = (write + 5) & mask; + + if ((x & 0xffffff80) == 0) { + buffer[write0] = x & 0x7f; + per_cpu(use_buffer_write, cpu) = write1; + } else if ((x & 0xffffc000) == 0) { + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) & 0x7f; + per_cpu(use_buffer_write, cpu) = write2; + } else if ((x & 0xffe00000) == 0) { + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) & 0x7f; + per_cpu(use_buffer_write, cpu) = write3; + } else if ((x & 0xf0000000) == 0) { + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) | 0x80; + buffer[write3] = (x>>21) & 0x7f; + per_cpu(use_buffer_write, cpu) = write4; + } else { + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) | 0x80; + buffer[write3] = (x>>21) | 0x80; + buffer[write4] = (x>>28) & 0x0f; + per_cpu(use_buffer_write, cpu) = write5; + } +} + +static void gator_buffer_write_bytes(int cpu, char *x, int len) +{ + uint32_t write = per_cpu(use_buffer_write, cpu); + uint32_t mask = use_buffer_mask; + char *buffer = per_cpu(use_buffer, cpu); + int i; + + for (i = 0; i < len; i++) { + buffer[write] = x[i]; + write = (write + 1) & mask; + } + + per_cpu(use_buffer_write, cpu) = write; +} + +static void gator_buffer_write_string(int cpu, char *x) +{ + int len = strlen(x); + gator_buffer_write_packed_int(cpu, len); + gator_buffer_write_bytes(cpu, x, len); +} + +static void gator_buffer_header(int cpu) +{ + gator_buffer_write_packed_int(cpu, PROTOCOL_FRAME); + gator_buffer_write_packed_int(cpu, cpu); + gator_buffer_write_packed_int(cpu, per_cpu(use_buffer_seq, cpu)); + per_cpu(use_buffer_seq, cpu)++; +} + +static void gator_buffer_commit(int cpu) +{ + buffer_commit_write(cpu, per_cpu(use_buffer_read, cpu), per_cpu(use_buffer_write, cpu)); + per_cpu(use_buffer_read, cpu) = per_cpu(use_buffer_write, cpu); + gator_buffer_header(cpu); + wake_up(&gator_buffer_wait); +} + +static void gator_buffer_check(int cpu, int tick) +{ + if (gator_sync_freq && !(tick % gator_sync_freq)) { + int c, sync; + spin_lock(&gator_commit_lock); + // synchronize, if all online cpus have the same tick waypoint + sync = per_cpu(gator_cpu_sync, cpu) = per_cpu(gator_cpu_tick, cpu); + for_each_online_cpu(c) { + if (sync != per_cpu(gator_cpu_sync, c)) { + sync = 0; + break; + } + } + if (sync) { + gator_buffer_write_packed_int(cpu, PROTOCOL_CPU_SYNC); + } + // commit the buffer + gator_buffer_commit(cpu); + spin_unlock(&gator_commit_lock); + } else { + int available = per_cpu(use_buffer_write, cpu) - per_cpu(use_buffer_read, cpu); + if (available < 0) { + available += use_buffer_size; + } + if (available >= ((use_buffer_size * 3) / 4)) { + spin_lock(&gator_commit_lock); + gator_buffer_commit(cpu); + spin_unlock(&gator_commit_lock); + } + } +} + +static void gator_add_trace(int cpu, unsigned int address) +{ + off_t offset = 0; + unsigned long cookie = get_address_cookie(cpu, current, address & ~1, &offset); + + if (cookie == NO_COOKIE || cookie == INVALID_COOKIE) { + offset = address; + } + + gator_buffer_write_packed_int(cpu, offset & ~1); + gator_buffer_write_packed_int(cpu, cookie); +} + +static void gator_add_sample(int cpu, struct pt_regs * const regs) +{ + int inKernel = regs ? !user_mode(regs) : 1; + unsigned long cookie = !inKernel ? get_exec_cookie(cpu, current) : NO_COOKIE; + + gator_buffer_write_packed_int(cpu, PROTOCOL_START_BACKTRACE); + + // TGID::PID::inKernel + gator_buffer_write_packed_int(cpu, cookie); + gator_buffer_write_packed_int(cpu, (unsigned int)current->tgid); + gator_buffer_write_packed_int(cpu, (unsigned int)current->pid); + gator_buffer_write_packed_int(cpu, inKernel); + + // get_irq_regs() will return NULL outside of IRQ context (e.g. nested IRQ) + if (regs) { + if (inKernel) { + gator_buffer_write_packed_int(cpu, PC_REG & ~1); + gator_buffer_write_packed_int(cpu, 0); // cookie + } else { + // Cookie+PC + gator_add_trace(cpu, PC_REG); + + // Backtrace + if (gator_backtrace_depth) + arm_backtrace_eabi(cpu, regs, gator_backtrace_depth); + } + } + + gator_buffer_write_packed_int(cpu, PROTOCOL_END_BACKTRACE); +} + +static void gator_write_packet(int cpu, int type, int len, int *buffer) +{ + int i; + gator_buffer_write_packed_int(cpu, type); + gator_buffer_write_packed_int(cpu, len); + for (i = 0; i < len; i++) { + gator_buffer_write_packed_int(cpu, buffer[i]); + } +} + +static void gator_write_annotate(int cpu, int len, int *buffer) +{ + int pos = 0; + + while (pos < len) { + unsigned int tid = buffer[pos++]; + unsigned int bytes = buffer[pos++]; + unsigned int words = (bytes + 3) / 4; + char *ptr = (char *)&buffer[pos]; + pos += words; + + gator_buffer_write_packed_int(cpu, PROTOCOL_ANNOTATE); + gator_buffer_write_packed_int(cpu, tid); + gator_buffer_write_packed_int(cpu, bytes); + gator_buffer_write_bytes(cpu, ptr, bytes); + } +} + +/****************************************************************************** + * Interrupt Processing + ******************************************************************************/ +static gator_interface *gi = NULL; + +static void gator_timer_interrupt(void) +{ + struct pt_regs * const regs = get_irq_regs(); + int cpu = raw_smp_processor_id(); + int *buffer, len, tick; + gator_interface *i; + + // check full backtrace has enough space, otherwise may + // have breaks between samples in the same callstack + if (per_cpu(gator_first_time, cpu)) { + per_cpu(gator_first_time, cpu) = 0; + + for (i = gi; i != NULL; i = i->next) { + if (i->read) { + i->read(NULL); + } + } + return; + } + + // Header + gator_buffer_write_packed_int(cpu, PROTOCOL_START_TICK); // Escape + + // Output scheduler + len = gator_trace_sched_read(&buffer); + if (len > 0) { + gator_write_packet(cpu, PROTOCOL_SCHEDULER_TRACE, len, buffer); + } + + // Output annotate + len = gator_annotate_read(&buffer); + if (len > 0) + gator_write_annotate(cpu, len, buffer); + + // Output counters + for (i = gi; i != NULL; i = i->next) { + if (i->read) { + len = i->read(&buffer); + if (len > 0) { + gator_write_packet(cpu, PROTOCOL_COUNTERS, len, buffer); + } + } + } + + // Output backtrace + gator_add_sample(cpu, regs); + + // Timer Tick + tick = per_cpu(gator_cpu_tick, cpu); + if (tick == gator_master_tick) { + tick++; + per_cpu(gator_cpu_tick, cpu) = gator_master_tick = tick; + } else { + per_cpu(gator_cpu_tick, cpu) = tick = gator_master_tick; + } + gator_write_packet(cpu, PROTOCOL_END_TICK, 1, &tick); + + // Check and commit; generally, commit is set to occur once per second + gator_buffer_check(cpu, tick); +} + +/****************************************************************************** + * hrtimer + ******************************************************************************/ +DEFINE_PER_CPU(struct hrtimer, percpu_hrtimer); +DEFINE_PER_CPU(int, hrtimer_is_active); +static int hrtimer_running; +static ktime_t profiling_interval; + +static enum hrtimer_restart gator_hrtimer_notify(struct hrtimer *hrtimer) +{ + hrtimer_forward_now(hrtimer, profiling_interval); + gator_timer_interrupt(); + return HRTIMER_RESTART; +} + +static int gator_timer_init(void) +{ + return 0; +} + +static void __gator_timer_offline(void *unused) +{ + int cpu = smp_processor_id(); + if (per_cpu(hrtimer_is_active, cpu)) { + gator_interface *i; + struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu); + hrtimer_cancel(hrtimer); + per_cpu(hrtimer_is_active, cpu) = 0; + gator_buffer_commit(cpu); + + // offline any events + for (i = gi; i != NULL; i = i->next) { + if (i->offline) { + i->offline(); + } + } + } +} + +static void gator_timer_offline(void) +{ + if (hrtimer_running) { + hrtimer_running = 0; + + on_each_cpu(__gator_timer_offline, NULL, 1); + + // output a final sync point + gator_buffer_write_packed_int(0, PROTOCOL_CPU_SYNC); + gator_buffer_commit(0); + } +} + +static void __gator_timer_online(void *unused) +{ + int cpu = smp_processor_id(); + if (!per_cpu(hrtimer_is_active, cpu)) { + gator_interface *i; + struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu); + hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer->function = gator_hrtimer_notify; + hrtimer_start(hrtimer, profiling_interval, HRTIMER_MODE_REL_PINNED); + per_cpu(gator_cpu_tick, cpu) = 0; + per_cpu(gator_first_time, cpu) = 1; + per_cpu(hrtimer_is_active, cpu) = 1; + + // online any events + for (i = gi; i != NULL; i = i->next) { + if (i->online) { + i->online(); + } + } + } +} + +int gator_timer_online(unsigned long setup) +{ + if (!setup) { + pr_err("gator: cannot start due to a system tick value of zero"); + return -1; + } else if (hrtimer_running) { + pr_notice("gator: high res timer already running"); + return 0; + } + + hrtimer_running = 1; + + // calculate profiling interval + profiling_interval = ns_to_ktime(1000000000UL / setup); + + // timer interrupt + gator_master_tick = 0; + on_each_cpu(__gator_timer_online, NULL, 1); + + return 0; +} + +static uint64_t gator_get_time(void) +{ + struct timespec ts; + uint64_t timestamp; + + ktime_get_ts(&ts); + timestamp = timespec_to_ns(&ts); + + return timestamp; +} + +/****************************************************************************** + * cpu online notifier + ******************************************************************************/ +static int __cpuinit gator_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + long cpu = (long)hcpu; + + switch (action) { + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + smp_call_function_single(cpu, __gator_timer_online, NULL, 1); + break; + case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: + smp_call_function_single(cpu, __gator_timer_offline, NULL, 1); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block __refdata gator_cpu_notifier = { + .notifier_call = gator_cpu_notify, +}; + +static int gator_notifier_start(void) +{ + return register_hotcpu_notifier(&gator_cpu_notifier); +} + +static void gator_notifier_stop(void) +{ + unregister_hotcpu_notifier(&gator_cpu_notifier); +} + +/****************************************************************************** + * Main + ******************************************************************************/ +int gator_event_install(int (*event_install)(gator_interface *)) +{ + gator_interface *ni = (gator_interface*)kmalloc(sizeof(gator_interface), GFP_KERNEL); + if (ni == NULL) { + return -1; + } + + ni->create_files = NULL; + ni->init = NULL; + ni->start = NULL; + ni->stop = NULL; + ni->online = NULL; + ni->offline = NULL; + ni->read = NULL; + ni->next = NULL; + + // Initialize ni gator interface + if (!event_install(ni)) { + if (gi == NULL) { + // Set gi to point to the first gator interface + gi = ni; + } else { + // Link the gator interfaces + gator_interface *i = gi; + while (i->next) { + i = i->next; + } + i->next = ni; + } + } else { + kfree(ni); + } + + return 0; +} + +static int gator_init(void) +{ + gator_interface *i; + int key = 0; + + if (gator_timer_init()) + return -1; + if (gator_trace_sched_init()) + return -1; + if (gator_annotate_init()) + return -1; + + // set up gator interface linked list structure + if (gator_events_install()) + return -1; + + // initialize all events + for (i = gi; i != NULL; i = i->next) { + if (i->init) { + if (i->init(&key)) { + return -1; + } + } + } + + return 0; +} + +static int gator_start(void) +{ + gator_interface *i, *f; + + // start all events + for (i = gi; i != NULL; i = i->next) { + if (i->start) { + if (i->start()) { + goto events_failure; + } + } + } + + if (gator_annotate_start()) + goto annotate_failure; + if (gator_trace_sched_start()) + goto sched_failure; + if (gator_timer_online(gator_timer_count)) + goto timer_failure; + if (gator_notifier_start()) + goto notifier_failure; + + return 0; + +notifier_failure: + gator_timer_offline(); +timer_failure: + gator_trace_sched_stop(); +sched_failure: + gator_annotate_stop(); +annotate_failure: +events_failure: + for (f = gi; f != i; f = f->next) { + f->stop(); + } + + return -1; +} + +static void gator_stop(void) +{ + gator_interface *i; + + // stop all events + for (i = gi; i != NULL; i = i->next) { + if (i->stop) { + i->stop(); + } + } + + gator_annotate_stop(); + gator_trace_sched_stop(); + + // stop all interrupt callback reads before tearing down other interfaces + gator_timer_offline(); + gator_notifier_stop(); +} + +static void gator_exit(void) +{ + gator_interface *i = gi; + + while (i) { + gator_interface *p = i; + i = i->next; + kfree(p); + } +} + +/****************************************************************************** + * Filesystem + ******************************************************************************/ +/* fopen("buffer") */ +static int gator_op_setup(void) +{ + int err = 0; + int cpu; + + mutex_lock(&start_mutex); + + use_buffer_size = gator_buffer_size; + use_buffer_mask = use_buffer_size - 1; + + // must be a power of 2 + if (use_buffer_size & (use_buffer_size - 1)) { + err = -ENOEXEC; + goto setup_error; + } + + gator_net_traffic = 0; + + gator_commit_read = gator_commit_write = 0; + gator_commit = vmalloc(COMMIT_SIZE * sizeof(int)); + if (!gator_commit) { + err = -ENOMEM; + goto setup_error; + } + + for_each_present_cpu(cpu) { + per_cpu(use_buffer, cpu) = vmalloc(use_buffer_size); + if (!per_cpu(use_buffer, cpu)) { + err = -ENOMEM; + goto setup_error; + } + + per_cpu(gator_cpu_sync, cpu) = 0; + per_cpu(gator_cpu_tick, cpu) = 0; + + per_cpu(use_buffer_seq, cpu) = 0; + per_cpu(use_buffer_read, cpu) = 0; + per_cpu(use_buffer_write, cpu) = 0; + gator_buffer_header(cpu); + } + +setup_error: + mutex_unlock(&start_mutex); + return err; +} + +/* Actually start profiling (echo 1>/dev/gator/enable) */ +static int gator_op_start(void) +{ + int err = 0; + + mutex_lock(&start_mutex); + + if (gator_started || gator_start()) + err = -EINVAL; + else + gator_started = 1; + + cookies_initialize(); + + mutex_unlock(&start_mutex); + + return err; +} + +/* echo 0>/dev/gator/enable */ +static void gator_op_stop(void) +{ + mutex_lock(&start_mutex); + + if (gator_started) { + gator_stop(); + + mutex_lock(&gator_buffer_mutex); + + gator_started = 0; + cookies_release(); + wake_up(&gator_buffer_wait); + + mutex_unlock(&gator_buffer_mutex); + } + + mutex_unlock(&start_mutex); +} + +static void gator_shutdown(void) +{ + int cpu; + + mutex_lock(&start_mutex); + + vfree(gator_commit); + gator_commit = NULL; + + for_each_present_cpu(cpu) { + mutex_lock(&gator_buffer_mutex); + vfree(per_cpu(use_buffer, cpu)); + per_cpu(use_buffer, cpu) = NULL; + per_cpu(use_buffer_seq, cpu) = 0; + per_cpu(use_buffer_read, cpu) = 0; + per_cpu(use_buffer_write, cpu) = 0; + mutex_unlock(&gator_buffer_mutex); + } + + mutex_unlock(&start_mutex); +} + +static int gator_set_backtrace(unsigned long val) +{ + int err = 0; + + mutex_lock(&start_mutex); + + if (gator_started) + err = -EBUSY; + else + gator_backtrace_depth = val; + + mutex_unlock(&start_mutex); + + return err; +} + +static ssize_t enable_read(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + return gatorfs_ulong_to_user(gator_started, buf, count, offset); +} + +static ssize_t enable_write(struct file *file, char const __user *buf, size_t count, loff_t *offset) +{ + unsigned long val; + int retval; + + if (*offset) + return -EINVAL; + + retval = gatorfs_ulong_from_user(&val, buf, count); + if (retval) + return retval; + + if (val) + retval = gator_op_start(); + else + gator_op_stop(); + + if (retval) + return retval; + return count; +} + +static const struct file_operations enable_fops = { + .read = enable_read, + .write = enable_write, +}; + +static int event_buffer_open(struct inode *inode, struct file *file) +{ + int err = -EPERM; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (test_and_set_bit_lock(0, &gator_buffer_opened)) + return -EBUSY; + + if ((err = gator_op_setup())) + goto fail; + + /* NB: the actual start happens from userspace + * echo 1 >/dev/gator/enable + */ + + return 0; + +fail: + __clear_bit_unlock(0, &gator_buffer_opened); + return err; +} + +static int event_buffer_release(struct inode *inode, struct file *file) +{ + gator_op_stop(); + gator_shutdown(); + __clear_bit_unlock(0, &gator_buffer_opened); + return 0; +} + +static ssize_t event_buffer_read(struct file *file, char __user *buf, + size_t count, loff_t *offset) +{ + int retval = -EINVAL; + int commit, length1, length2, read; + char *buffer1, *buffer2; + int cpu; + + /* do not handle partial reads */ + if (count != use_buffer_size || *offset) + return -EINVAL; + + // sleep until the condition is true or a signal is received + // the condition is checked each time gator_buffer_wait is woken up + wait_event_interruptible(gator_buffer_wait, buffer_commit_ready() || !gator_started); + + if (signal_pending(current)) + return -EINTR; + + if (!buffer_commit_ready()) + return 0; + + buffer_commit_read(&cpu, &read, &commit); + + mutex_lock(&gator_buffer_mutex); + + retval = -EFAULT; + + /* May happen if the buffer is freed during pending reads. */ + if (!per_cpu(use_buffer, cpu)) { + retval = -EFAULT; + goto out; + } + + /* determine the size of two halves */ + length1 = commit - read; + length2 = 0; + buffer1 = &(per_cpu(use_buffer, cpu)[read]); + buffer2 = &(per_cpu(use_buffer, cpu)[0]); + if (length1 < 0) { + length1 = use_buffer_size - read; + length2 = commit; + } + + /* start, middle or end */ + if (length1 > 0) { + if (copy_to_user(&buf[0], buffer1, length1)) { + goto out; + } + } + + /* possible wrap around */ + if (length2 > 0) { + if (copy_to_user(&buf[length1], buffer2, length2)) { + goto out; + } + } + + retval = length1 + length2; + + /* kick just in case we've lost an SMP event */ + wake_up(&gator_buffer_wait); + +out: + // do not adjust network stats if in non-streaming buffer mode + if (gator_sync_freq) + gator_net_traffic += retval; + mutex_unlock(&gator_buffer_mutex); + return retval; +} + +const struct file_operations gator_event_buffer_fops = { + .open = event_buffer_open, + .release = event_buffer_release, + .read = event_buffer_read, +}; + +static ssize_t depth_read(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + return gatorfs_ulong_to_user(gator_backtrace_depth, buf, count, + offset); +} + +static ssize_t depth_write(struct file *file, char const __user *buf, size_t count, loff_t *offset) +{ + unsigned long val; + int retval; + + if (*offset) + return -EINVAL; + + retval = gatorfs_ulong_from_user(&val, buf, count); + if (retval) + return retval; + + retval = gator_set_backtrace(val); + + if (retval) + return retval; + return count; +} + +static const struct file_operations depth_fops = { + .read = depth_read, + .write = depth_write +}; + +static const char gator_cpu_type[] = "gator"; + +static ssize_t cpu_type_read(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + return gatorfs_str_to_user(gator_cpu_type, buf, count, offset); +} + +static const struct file_operations cpu_type_fops = { + .read = cpu_type_read, +}; + +void gator_op_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + gator_interface *i; + int cpu; + + /* reinitialize default values */ + gator_cpu_cores = 0; + for_each_present_cpu(cpu) { + gator_cpu_cores++; + } + gator_buffer_size = BUFFER_SIZE_DEFAULT; + gator_sync_freq = SYNC_FREQ_DEFAULT; + + gatorfs_create_file(sb, root, "enable", &enable_fops); + gatorfs_create_file(sb, root, "buffer", &gator_event_buffer_fops); + gatorfs_create_file(sb, root, "backtrace_depth", &depth_fops); + gatorfs_create_file(sb, root, "cpu_type", &cpu_type_fops); + gatorfs_create_ulong(sb, root, "cpu_cores", &gator_cpu_cores); + gatorfs_create_ulong(sb, root, "buffer_size", &gator_buffer_size); + gatorfs_create_ulong(sb, root, "tick", &gator_timer_count); + gatorfs_create_ulong(sb, root, "sync_freq", &gator_sync_freq); + gatorfs_create_ro_ulong(sb, root, "version", &gator_protocol_version); + + // Annotate interface + gator_annotate_create_files(sb, root); + + // Linux Events + dir = gatorfs_mkdir(sb, root, "events"); + for (i = gi; i != NULL; i = i->next) { + if (i->create_files) { + i->create_files(sb, dir); + } + } +} + +/****************************************************************************** + * Module + ******************************************************************************/ +static int gator_initialized; + +static int __init gator_module_init(void) +{ + if (gatorfs_register()) { + return -1; + } + + if (gator_init()) { + gatorfs_unregister(); + return -1; + } + + gator_initialized = 1; +#ifdef GATOR_DEBUG + pr_err("gator_module_init"); +#endif + return 0; +} + +static void __exit gator_module_exit(void) +{ +#ifdef GATOR_DEBUG + pr_err("gator_module_exit"); +#endif + tracepoint_synchronize_unregister(); + gatorfs_unregister(); + if (gator_initialized) { + gator_initialized = 0; + gator_exit(); + } +} + +module_init(gator_module_init); +module_exit(gator_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("ARM Ltd"); +MODULE_DESCRIPTION("Gator system profiler"); diff --git a/driver/gator_trace_sched.c b/driver/gator_trace_sched.c new file mode 100644 index 0000000..19d8d89 --- /dev/null +++ b/driver/gator_trace_sched.c @@ -0,0 +1,263 @@ +/** + * Copyright (C) ARM Limited 2010-2011. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include "gator.h" + +#define SCHED_TIMER_EVENT 0 +#define SCHED_WAIT_TASK 1 +#define SCHED_WAKEUP 2 +#define SCHED_WAKEUP_NEW 3 +#define SCHED_SWITCH 4 +#define SCHED_MIGRATE_TASK 5 +#define SCHED_PROCESS_FREE 6 +#define SCHED_PROCESS_EXIT 7 +#define SCHED_PROCESS_WAIT 8 +#define SCHED_PROCESS_FORK 9 +#define SCHED_OVERFLOW -1 + +#define SCHEDSIZE (16*1024) + +static DEFINE_PER_CPU(int *[2], theSchedBuf); +static DEFINE_PER_CPU(int, theSchedSel); +static DEFINE_PER_CPU(int, theSchedPos); +static DEFINE_PER_CPU(int, theSchedErr); + +static void probe_sched_write(int type, int param1, int param2, int param3) +{ + unsigned long flags; + int cpu = smp_processor_id(); + uint64_t time = gator_get_time(); + int *schedBuf; + int schedPos; + + if (!per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)]) + return; + + // disable interrupts to synchronize with gator_trace_sched_read(); spinlocks not needed since percpu buffers are used + local_irq_save(flags); + + schedPos = per_cpu(theSchedPos, cpu); + schedBuf = per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)]; + + if (schedPos < (SCHEDSIZE-100)) { + // capture + schedBuf[schedPos+0] = type; + schedBuf[schedPos+1] = (int)time; + schedBuf[schedPos+2] = (int)(time >> 32); + schedBuf[schedPos+3] = param1; + schedBuf[schedPos+4] = param2; + schedBuf[schedPos+5] = param3; + per_cpu(theSchedPos, cpu) = schedPos + 6; + } else if (!per_cpu(theSchedErr, cpu)) { + per_cpu(theSchedErr, cpu) = 1; + schedBuf[schedPos+0] = SCHED_OVERFLOW; + schedBuf[schedPos+1] = 0; + schedBuf[schedPos+2] = 0; + schedBuf[schedPos+3] = 0; + schedBuf[schedPos+4] = 0; + schedBuf[schedPos+5] = 0; + per_cpu(theSchedPos, cpu) = schedPos + 6; + pr_debug("gator: tracepoint overflow\n"); + } + local_irq_restore(flags); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +GATOR_DEFINE_PROBE(sched_wait_task, TP_PROTO(struct rq *rq, struct task_struct *p)) +#else +GATOR_DEFINE_PROBE(sched_wait_task, TP_PROTO(struct task_struct *p)) +#endif +{ + probe_sched_write(SCHED_WAIT_TASK, 0, p->pid, 0); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +GATOR_DEFINE_PROBE(sched_wakeup, TP_PROTO(struct rq *rq, struct task_struct *p, int success)) +#else +GATOR_DEFINE_PROBE(sched_wakeup, TP_PROTO(struct task_struct *p, int success)) +#endif +{ + if (success) + probe_sched_write(SCHED_WAKEUP, 0, p->pid, 0); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +GATOR_DEFINE_PROBE(sched_wakeup_new, TP_PROTO(struct rq *rq, struct task_struct *p, int success)) +#else +GATOR_DEFINE_PROBE(sched_wakeup_new, TP_PROTO(struct task_struct *p, int success)) +#endif +{ + if (success) + probe_sched_write(SCHED_WAKEUP_NEW, 0, p->tgid, p->pid); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next)) +#else +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next)) +#endif +{ + probe_sched_write(SCHED_SWITCH, (int)next, next->tgid, next->pid); +} + +GATOR_DEFINE_PROBE(sched_migrate_task, TP_PROTO(struct task_struct *p, int dest_cpu)) +{ + probe_sched_write(SCHED_MIGRATE_TASK, 0, dest_cpu, p->pid); +} + +GATOR_DEFINE_PROBE(sched_process_free, TP_PROTO(struct task_struct *p)) +{ + probe_sched_write(SCHED_PROCESS_FREE, 0, p->pid, 0); +} + +GATOR_DEFINE_PROBE(sched_process_exit, TP_PROTO(struct task_struct *p)) +{ + probe_sched_write(SCHED_PROCESS_EXIT, 0, p->pid, 0); +} + +GATOR_DEFINE_PROBE(sched_process_wait, TP_PROTO(struct pid *pid)) +{ + probe_sched_write(SCHED_PROCESS_WAIT, 0, pid_nr(pid), 0); +} + +GATOR_DEFINE_PROBE(sched_process_fork, TP_PROTO(struct task_struct *parent, struct task_struct *child)) +{ + probe_sched_write(SCHED_PROCESS_FORK, (int)child, parent->pid, child->pid); +} + +int gator_trace_sched_init(void) +{ + return 0; +} + +int gator_trace_sched_start(void) +{ + int cpu; + + for_each_present_cpu(cpu) { + per_cpu(theSchedSel, cpu) = 0; + per_cpu(theSchedPos, cpu) = 0; + per_cpu(theSchedErr, cpu) = 0; + per_cpu(theSchedBuf, cpu)[0] = kmalloc(SCHEDSIZE * sizeof(int), GFP_KERNEL); + per_cpu(theSchedBuf, cpu)[1] = kmalloc(SCHEDSIZE * sizeof(int), GFP_KERNEL); + if (!per_cpu(theSchedBuf, cpu)) + return -1; + } + + // register tracepoints + if (GATOR_REGISTER_TRACE(sched_wait_task)) + goto fail_sched_wait_task; + if (GATOR_REGISTER_TRACE(sched_wakeup)) + goto fail_sched_wakeup; + if (GATOR_REGISTER_TRACE(sched_wakeup_new)) + goto fail_sched_wakeup_new; + if (GATOR_REGISTER_TRACE(sched_switch)) + goto fail_sched_switch; + if (GATOR_REGISTER_TRACE(sched_migrate_task)) + goto fail_sched_migrate_task; + if (GATOR_REGISTER_TRACE(sched_process_free)) + goto fail_sched_process_free; + if (GATOR_REGISTER_TRACE(sched_process_exit)) + goto fail_sched_process_exit; + if (GATOR_REGISTER_TRACE(sched_process_wait)) + goto fail_sched_process_wait; + if (GATOR_REGISTER_TRACE(sched_process_fork)) + goto fail_sched_process_fork; + pr_debug("gator: registered tracepoints\n"); + + return 0; + + // unregister tracepoints on error +fail_sched_process_fork: + GATOR_UNREGISTER_TRACE(sched_process_wait); +fail_sched_process_wait: + GATOR_UNREGISTER_TRACE(sched_process_exit); +fail_sched_process_exit: + GATOR_UNREGISTER_TRACE(sched_process_free); +fail_sched_process_free: + GATOR_UNREGISTER_TRACE(sched_migrate_task); +fail_sched_migrate_task: + GATOR_UNREGISTER_TRACE(sched_switch); +fail_sched_switch: + GATOR_UNREGISTER_TRACE(sched_wakeup_new); +fail_sched_wakeup_new: + GATOR_UNREGISTER_TRACE(sched_wakeup); +fail_sched_wakeup: + GATOR_UNREGISTER_TRACE(sched_wait_task); +fail_sched_wait_task: + pr_err("gator: tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +void gator_trace_sched_stop(void) +{ + int cpu; + GATOR_UNREGISTER_TRACE(sched_wait_task); + GATOR_UNREGISTER_TRACE(sched_wakeup); + GATOR_UNREGISTER_TRACE(sched_wakeup_new); + GATOR_UNREGISTER_TRACE(sched_switch); + GATOR_UNREGISTER_TRACE(sched_migrate_task); + GATOR_UNREGISTER_TRACE(sched_process_free); + GATOR_UNREGISTER_TRACE(sched_process_exit); + GATOR_UNREGISTER_TRACE(sched_process_wait); + GATOR_UNREGISTER_TRACE(sched_process_fork); + pr_debug("gator: unregistered tracepoints\n"); + + for_each_present_cpu(cpu) { + kfree(per_cpu(theSchedBuf, cpu)[0]); + kfree(per_cpu(theSchedBuf, cpu)[1]); + per_cpu(theSchedBuf, cpu)[0] = NULL; + per_cpu(theSchedBuf, cpu)[1] = NULL; + } +} + +int gator_trace_sched_read(int **buffer) +{ + uint64_t time = gator_get_time(); + int cpu = smp_processor_id(); + unsigned long flags; + int *schedBuf; + int schedPos; + int i; + + if (!per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)]) + return 0; + + local_irq_save(flags); + + schedBuf = per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)]; + schedPos = per_cpu(theSchedPos, cpu); + + per_cpu(theSchedSel, cpu) = !per_cpu(theSchedSel, cpu); + per_cpu(theSchedPos, cpu) = 0; + per_cpu(theSchedErr, cpu) = 0; + + local_irq_restore(flags); + + // find mm and replace with cookies + for (i = 0; i < schedPos; i += 6) { + uint32_t cookie = schedBuf[i+3]; + if (cookie) { + struct task_struct *task = (struct task_struct *)cookie; + schedBuf[i+3] = get_exec_cookie(cpu, task); + } + } + + // timer/end event + schedBuf[schedPos++] = SCHED_TIMER_EVENT; + schedBuf[schedPos++] = (int)time; + schedBuf[schedPos++] = (int)(time >> 32); + + if (buffer) + *buffer = schedBuf; + + return schedPos; +} -- cgit v1.2.3