aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDrew Richardson <drew.richardson@arm.com>2011-02-02 12:00:00 -0800
committerDrew Richardson <drew.richardson@arm.com>2014-12-19 15:04:11 -0800
commita3a50f5d7331c6294a81039d1eae731ed5682521 (patch)
treeede6b1fa3a29945b010e190289493704af8fdb37
gator: Version 5.45.4
Signed-off-by: Drew Richardson <drew.richardson@arm.com>
-rw-r--r--.gitignore7
-rw-r--r--README_Streamline.txt81
-rw-r--r--driver/LICENSE339
-rw-r--r--driver/Makefile26
-rw-r--r--driver/gator.h72
-rw-r--r--driver/gator_annotate.c135
-rw-r--r--driver/gator_backtrace.c68
-rw-r--r--driver/gator_cookies.c224
-rw-r--r--driver/gator_events.c40
-rw-r--r--driver/gator_events_armv6.c233
-rw-r--r--driver/gator_events_armv7.c411
-rw-r--r--driver/gator_events_block.c174
-rw-r--r--driver/gator_events_irq.c174
-rw-r--r--driver/gator_events_meminfo.c197
-rw-r--r--driver/gator_events_net.c176
-rw-r--r--driver/gator_events_sched.c116
-rw-r--r--driver/gator_fs.c270
-rw-r--r--driver/gator_main.c1071
-rw-r--r--driver/gator_trace_sched.c263
19 files changed, 4077 insertions, 0 deletions
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- <platform_defconfig> (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
+ - [*] <SoC name> 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 <kernel_build_dir> 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 <installdir>/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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 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 <kernel_build_dir> 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 <linux/version.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+
+/******************************************************************************
+ * 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 <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <asm/current.h>
+#include <linux/spinlock.h>
+
+#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 <trace/events/block.h>
+
+#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 <trace/events/irq.h>
+
+#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 <linux/workqueue.h>
+#include <trace/events/kmem.h>
+
+#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 <linux/netdevice.h>
+
+#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 <trace/events/sched.h>
+
+#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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <asm/uaccess.h>
+
+#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 <linux/slab.h>
+#include <linux/cpu.h>
+#include <linux/sched.h>
+#include <linux/irq.h>
+#include <linux/vmalloc.h>
+#include <asm/uaccess.h>
+
+#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 <trace/events/sched.h>
+#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;
+}