summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhunt <hunt>2005-06-18 07:24:42 +0000
committerhunt <hunt>2005-06-18 07:24:42 +0000
commite5d2abb56ca57a613e4ef9398a6499190b2265be (patch)
tree083465047d73bc498f2278d2eb3dc8514b455ad3
parentef0e92b0335077884edd5ac9997a203bec1839b0 (diff)
downloadsystemtap-steved-e5d2abb56ca57a613e4ef9398a6499190b2265be.tar.gz
systemtap-steved-e5d2abb56ca57a613e4ef9398a6499190b2265be.tar.xz
systemtap-steved-e5d2abb56ca57a613e4ef9398a6499190b2265be.zip
2005-06-18 Martin Hunt <hunt@redhat.com>
* counter.c: New file. Counter aggregations. * stat.c: New file. Stat aggregations. * stat.h: Header file for stats. * map-int.c: New file. Support for int64 values. * map-stat.c: New file. Support for stat values. * map-str.c: New file. Support for string values. * map-values.c: Now just includes the necessary map-*.c files. * stat-common.c: New file. Stats stuff common to Stats and maps containing stats. * Doxyfile: Bumped version to 0.6. * README: Renamed README.doc and reorganized.
-rw-r--r--runtime/ChangeLog24
-rw-r--r--runtime/Doxyfile14
-rw-r--r--runtime/README143
-rw-r--r--runtime/README.doc176
-rw-r--r--runtime/counter.c136
-rw-r--r--runtime/docs/examples/argv.c5
-rw-r--r--runtime/docs/examples/foreach.c10
-rw-r--r--runtime/docs/examples/list.c7
-rw-r--r--runtime/docs/examples/map.c2
-rw-r--r--runtime/io.c4
-rw-r--r--runtime/list.c3
-rw-r--r--runtime/map-int.c73
-rw-r--r--runtime/map-keys.c9
-rw-r--r--runtime/map-stat.c84
-rw-r--r--runtime/map-str.c118
-rw-r--r--runtime/map-values.c156
-rw-r--r--runtime/map.c429
-rw-r--r--runtime/map.doc129
-rw-r--r--runtime/map.h37
-rw-r--r--runtime/print.c40
-rw-r--r--runtime/stat-common.c229
-rw-r--r--runtime/stat.c254
-rw-r--r--runtime/stat.h37
-rw-r--r--runtime/string.c30
-rw-r--r--runtime/string.h30
25 files changed, 1423 insertions, 756 deletions
diff --git a/runtime/ChangeLog b/runtime/ChangeLog
index b40ae666..6f8246b6 100644
--- a/runtime/ChangeLog
+++ b/runtime/ChangeLog
@@ -1,3 +1,27 @@
+2005-06-18 Martin Hunt <hunt@redhat.com>
+
+ * counter.c: New file. Counter aggregations.
+
+ * stat.c: New file. Stat aggregations.
+
+ * stat.h: Header file for stats.
+
+ * map-int.c: New file. Support for int64 values.
+
+ * map-stat.c: New file. Support for stat values.
+
+ * map-str.c: New file. Support for string values.
+
+ * map-values.c: Now just includes the necessary map-*.c files.
+
+ * stat-common.c: New file. Stats stuff common to Stats and maps
+ containing stats.
+
+ * Doxyfile: Bumped version to 0.6.
+
+ * README: Renamed README.doc and reorganized.
+
+
2005-06-15 Martin Hunt <hunt@redhat.com>
* current.c (_stp_ret_addr): Fix computation for i386.
diff --git a/runtime/Doxyfile b/runtime/Doxyfile
index cebc99b3..fcf4f694 100644
--- a/runtime/Doxyfile
+++ b/runtime/Doxyfile
@@ -23,7 +23,7 @@ PROJECT_NAME = "SystemTap Runtime"
# This could be handy for archiving the generated documentation or
# if some version control system is used.
-PROJECT_NUMBER = 0.5
+PROJECT_NUMBER = 0.6
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
@@ -432,7 +432,7 @@ INPUT =
# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm
-FILE_PATTERNS = *.c *.h README TODO map.doc
+FILE_PATTERNS = *.c *.h README.doc TODO map.doc
# The RECURSIVE tag can be used to turn specify whether or not subdirectories
# should be searched for input files as well. Possible values are YES and NO.
@@ -456,7 +456,7 @@ EXCLUDE_SYMLINKS = NO
# certain files from those directories.
EXCLUDE_PATTERNS = Makefile.* ChangeLog CHANGES CHANGES.* \
- README.* *.png AUTHORS DESIGN DESIGN.* *.desktop \
+ README *.png AUTHORS DESIGN DESIGN.* *.desktop \
DESKTOP* COMMENTS HOWTO magic NOTES THANKS Doxyfile
@@ -464,7 +464,7 @@ EXCLUDE_PATTERNS = Makefile.* ChangeLog CHANGES CHANGES.* \
# directories that contain example code fragments that are included (see
# the \include command).
-EXAMPLE_PATH = docs/examples probes
+EXAMPLE_PATH = docs/examples probes tests
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
@@ -961,7 +961,7 @@ INCLUDE_FILE_PATTERNS =
# undefined via #undef or recursively expanded use the := operator
# instead of the = operator.
-PREDEFINED = __i386__
+PREDEFINED = __i386__ NEED_INT64_VALS NEED_STRING_VALS NEED_STAT_VALS KEY1_TYPE_INT64
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
# this tag can be used to specify a list of macro names that should be expanded.
@@ -1044,7 +1044,7 @@ HIDE_UNDOC_RELATIONS = YES
# toolkit from AT&T and Lucent Bell Labs. The other options in this section
# have no effect if this option is set to NO (the default)
-HAVE_DOT = NO
+HAVE_DOT = YES
# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
@@ -1091,7 +1091,7 @@ INCLUDED_BY_GRAPH = YES
# So in most cases it will be better to enable call graphs for selected
# functions only using the \callgraph command.
-CALL_GRAPH = NO
+CALL_GRAPH = YES
# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
# will graphical hierarchy of all classes instead of a textual one.
diff --git a/runtime/README b/runtime/README
index aecb3282..470cad16 100644
--- a/runtime/README
+++ b/runtime/README
@@ -1,142 +1,9 @@
-/** @mainpage SystemTap Runtime
-
-@section intro_sec Introduction
-
-This document describes the implementation of the SystemTap Runtime. It is intended for developers
-of the SystemTap Language translator, TapSet authors or kprobes programmers.
-These functions are not directly available from the SystemTap Language.
-
-The SystemTap Runtime Library consists of all functions
-and code fragments needed by the compiler/translator to
-include in building a kernel module using kprobes. It
-also include I/O code to transmit its output from the kernel to userspace.
-
-In addition to the library, the runtime includes a SystemTap user-space daemon
-(stpd). Stpd grabs data sent from the I/O code in the runtime and displays it
-and/or saves it to files. Stpd will handle other issues like
-inserting and removing modules.
-
-Stpd and the I/O code make use of both relayfs and netlink for communication. For
-kernels without relayfs builtin, it is provided as a standalone module under the runtime directory.
-
-@section design_sec Design
-@subsection impl_sec Implementation
-The library is written in C and is really not a library but a collection of code
-that can be conditionally included in a modules. This allows for a high degree of
-customization and efficiency.
-
-@subsection map_sec Maps (Associative Arrays)
-Maps are implemented as hash lists. In order to efficiently handle maps with up to five keys of any combination
-of int64s and strings, the functions to create maps, set keys, and set and get values
-are all generated as needed. To do this, you must simply do some defines and
-include the proper files. For example, if you need a map with 3 keys, int64, int64, and string
-that stores strings, you need to do
-@code
-#define KEY1_TYPE INT64
-#define KEY2_TYPE INT64
-#define KEY3_TYPE STRING
-#include "map-keys.c"
-
-#define VALUE_TYPE STRING
-#include "map-values.c"
-
-#include "map.c"
-@endcode
-
-In the above example, maps are created with _stp_map_new_int64_int64_str(). All the
-generated functions start with a base name (such as _stp_map_new) and add the key types separated
-by underscores.
-
-Each map can hold only one type of data; int64, string, or statistics. Each element belonging to a map can have up to
-5 keys and a value. Implemented key types are strings and int64.
-
-To simplify the implementation, the functions to set the key and the functions to set the data are separated.
-The map remembers what the current key is.
-For example, to continue our previous example,
-@code
-/* create a map with a max of 100 elements containing strings */
-MAP mymap = _stp_map_new_int64_int64_str(100, STRING);
-
-/* mymap[100, 300, "Ohio"] = "Columbus" */
-_stp_map_key_int64_int64_str (mymap, 100, 300, "Ohio");
-_stp_map_set_str (mymap, "Columbus");
-@endcode
-
-All elements have a default value of 0 (or NULL). Elements are only saved to the map when their value is set
-to something nonzero.
-
-For good examples, see the runtime/tests/maps directory or the example probes.
-
-@subsection list_sec Lists
-A list is a special map which has internally ascending int64 keys. Adding a value to
-a list does not require setting a key first. Create a list with _stp_list_new(). Add to it
-with _stp_list_add_str() and _stp_list_add_int64(). Clear it with _stp_list_clear().
-
-@subsection string_sec Strings
-
-One of the biggest restrictions the library has is that it cannot allocate things like strings off the stack.
-It is also not a good idea to dynamically allocate space for strings with kmalloc(). That leaves us with
-statically allocated space for strings. This is what is implemented in the String module. Strings use
-preallocated per-cpu buffers and are safe to use (unlike C strings).
-
-@subsection io_sec I/O
-Generally things are written to a "print buffer" using the internal
-functions _stp_print_xxx().
-\code
-_stp_print ("Output is: ");
-_stp_printf ("pid is %d ", current->pid);
-_stp_printf ("name is %s", current->comm);
-\endcode
-before the probe returns it must call _stp_print_flush(). This
-timestamps the accumulated print buffer and sends it to relayfs.
-When relayfs fills an internal buffer, the user-space daemon is notified
-data is ready and reads a bug per-cpu chunk, which contains a line like:
-\verbatim
-[123456.000002] Output is: pid is 1234 name is bash
-\endverbatim
-
-The user-daemon (stpd) saves this data to a file named something like
-"stpd_cpu2". When the user hits ^c, a timer expires, or the probe
-module notifies stpd (through a netlink command channel) that it wants
-to terminate, stpd does "system(rmmod)" then collects the last output
-before exiting.
-As an option, if we don't need bulk per-cpu data, we can put
-\code
-#define STP_NETLINK_ONLY
-\endcode
-at the top of the module and all output will go over a netlink channel.
-In the SystemTap language, we will provide some simple functions to control the buffering policy, which
-will control the use of netlink and parameters to relayfs and stpd.
-
-
-@section started Getting Started
-@verbatim
->cd stp/src/runtime
->cd stpd
-> make
->cd ../relayfs
-> make
-> cd ../transport
-make
-> cd ../probes/shellsnoop
+To build everything (relayfs, transport, stpd, and example probes):
> make
-> su
-> ./stp shellsnoop.ko
-@endverbatim
-
-When building the example probes, ignore the warnings about relayfs and
-_stp_ctrl_xxx functions undefined. These are in other modules.
-
-Hit ^C to exit from a probe.
-
-@section probe_sec Example Probes
-Working sample probe code using the runtime is in runtime/probes.
-<a href="dir_000000.html"> Browse probes.</a>
+To clean up:
+> make clean
-@section todo_sec ToDo
-\link todo Click Here for Complete List \endlink
+To build docs
+> doxygen Doxyfile
-@section links Links
-<a href="http://sources.redhat.com/systemtap/">SystemTap Project Page</a>
- */
diff --git a/runtime/README.doc b/runtime/README.doc
new file mode 100644
index 00000000..12d0aad0
--- /dev/null
+++ b/runtime/README.doc
@@ -0,0 +1,176 @@
+/** @mainpage SystemTap Runtime
+
+@section intro_sec Introduction
+
+This document describes the implementation of the SystemTap Runtime. It is intended for developers
+of the SystemTap Language translator, TapSet authors or kprobes programmers.
+These functions are not directly available from the SystemTap Language.
+
+The SystemTap Runtime Library consists of all functions
+and code fragments needed by the compiler/translator to
+include in building a kernel module using kprobes. It
+also includes I/O code to transmit its output from the kernel to userspace.
+
+In addition to the library, the runtime includes a SystemTap user-space daemon
+(stpd). Stpd grabs data sent from the I/O code in the runtime and displays it
+and/or saves it to files. Stpd will handle other issues like
+inserting and removing modules.
+
+Stpd and the I/O code make use of both relayfs and netlink for communication. For
+kernels without relayfs builtin, it is provided as a standalone module under the runtime directory.
+
+@ref start_page
+
+@ref maps
+
+@ref lists
+
+@ref string
+
+@ref counter
+
+@ref stat
+
+@ref io_page
+
+<a href="http://sources.redhat.com/systemtap/"><b>SystemTap Project Page</b></a>
+*/
+
+/**
+@page start_page Getting Started
+The library is written in C and is really not a library but a collection of code
+that can be conditionally included in a modules. This allows for a high degree of
+customization and efficiency. To get started, first compile everything.
+
+@verbatim
+>cd stp/src/runtime
+> make
+make -w -C relayfs
+make[1]: Entering directory `/home/hunt/stp/src/runtime/relayfs'
+make -C /lib/modules/2.6.11-1.27_FC3.huntsmp/build SUBDIRS=/home/hunt/stp/src/runtime/relayfs modules
+make[2]: Entering directory `/lib/modules/2.6.11-1.27_FC3.huntsmp/build'
+ Building modules, stage 2.
+ MODPOST
+make[2]: Leaving directory `/lib/modules/2.6.11-1.27_FC3.huntsmp/build'
+make[1]: Leaving directory `/home/hunt/stp/src/runtime/relayfs'
+make -w -C transport
+make[1]: Entering directory `/home/hunt/stp/src/runtime/transport'
+make -C /lib/modules/2.6.11-1.27_FC3.huntsmp/build include SUBDIRS=/home/hunt/stp/src/runtime/transport modules
+make[2]: Entering directory `/lib/modules/2.6.11-1.27_FC3.huntsmp/build'
+make[2]: Nothing to be done for `include'.
+ Building modules, stage 2.
+ MODPOST
+make[2]: Leaving directory `/lib/modules/2.6.11-1.27_FC3.huntsmp/build'
+make[1]: Leaving directory `/home/hunt/stp/src/runtime/transport'
+make -w -C stpd
+make[1]: Entering directory `/home/hunt/stp/src/runtime/stpd'
+make[1]: Nothing to be done for `all'.
+make[1]: Leaving directory `/home/hunt/stp/src/runtime/stpd'
+cd probes; ./build
+Building shellsnoop
+Building test4
+Building where_func
+Building scf
+@endverbatim
+
+You might want to test things out. To do this:
+@verbatim
+> cd probes/shellsnoop
+> su
+> ./stp shellsnoop.ko
+@endverbatim
+Now from another shell, type some commands. You should see information about the command displayed
+in the shellsnoop window.
+
+Hit ^C to exit from a probe.
+
+To write your own probe, look through the example probes. You can start writing your
+own by editing this one or one of the examples.
+
+@include template.c
+*/
+
+/** @page io_page I/O
+Generally things are written to a "print buffer" using the internal
+functions _stp_print_xxx().
+@code
+_stp_print ("Output is: ");
+_stp_printf ("pid is %d ", current->pid);
+_stp_printf ("name is %s", current->comm);
+@endcode
+before the probe returns it must call _stp_print_flush().
+<b>(This behaviour is expected to change soon!)</b>
+This timestamps the accumulated print buffer and sends it to relayfs.
+When relayfs fills an internal buffer, the user-space daemon is notified
+data is ready and reads a big per-cpu chunk.
+
+The user-daemon (stpd) saves this data to a file named something like
+"stpd_cpu2". When the user hits ^c, a timer expires, or the probe
+module notifies stpd (through a netlink command channel) that it wants
+to terminate, stpd does "system(rmmod)" then collects the last output
+before exiting.
+As an option, if we don't need bulk per-cpu data, we can put
+\code
+#define STP_NETLINK_ONLY
+\endcode
+at the top of the module and all output will go over a netlink channel.
+In the SystemTap language, we will provide some simple functions to control the buffering policy, which
+will control the use of netlink and parameters to relayfs and stpd.
+
+*/
+
+/** @addtogroup string
+
+One of the biggest restrictions the library has is that it cannot put large things like
+strings on the stack. And allocating memory from within a probe is a very bad idea.
+It is also not a good idea to dynamically allocate space for strings with kmalloc(). That leaves us with
+statically allocated space for strings. This is what is implemented in the String module. Strings use
+preallocated per-cpu buffers and are safe to use (unlike C strings) because operations which manipulate
+them all check to prevent overflowing their allocated space.
+
+Every probe function that uses a string needs to call _stp_string_init() with
+a unique number. For example, here is a bit of code that grabs a string, puts a stack
+trace into it and then uses that for a map key.
+
+@code
+ String str = _stp_string_init (0);
+ _stp_stack_sprint (str, regs);
+ _stp_map_key_str(map1, _stp_string_ptr(str));
+@endcode
+
+If the same probe needs a another String, you would create it with
+@code
+ String str = _stp_string_init (1);
+@endcode
+
+Be sure to declare how many strings you need the top of your sources, before including "runtime.h".
+@code
+#define STP_NUM_STRINGS 2
+@endcode
+
+Note that STP_NUM_STRINGS should be set to the maximum number of strings a probe needs.
+Probes are per-cpu so many different probes can all call _stp_string_init(0) without fear that
+of collisions. Each CPU that could be in a probe will get a different buffer for String 0.
+*/
+
+/** @addtogroup lists
+A list is a special map which has internally ascending int64 keys. Adding a value to
+a list does not require setting a key first.
+
+Create a list with _stp_list_new().
+
+Add to it with _stp_list_add_str() or _stp_list_add_int64().
+
+You can read the list by calling _stp_list_size() and then using _stp_map_key_int64() with
+keys from 0 to the size. Or better yet, use foreach().
+
+For example, if you have a list of strings called "mylist"
+@code
+struct map_node *ptr;
+foreach (mylist, ptr)
+ _stp_print(_stp_get_str(ptr));
+
+@endcode
+
+Clear it with _stp_list_clear().
+*/
diff --git a/runtime/counter.c b/runtime/counter.c
new file mode 100644
index 00000000..ff50c766
--- /dev/null
+++ b/runtime/counter.c
@@ -0,0 +1,136 @@
+#ifndef _COUNTER_C_ /* -*- linux-c -*- */
+#define _COUNTER_C_
+
+/** @file counter.c
+ * @brief Counter Aggregation
+ */
+
+/** @addtogroup counter Counter Aggregation
+ * This is a 64-bit per-cpu Counter. It is much more efficient than an atomic counter
+ * because there is no contention between processors and caches in an SMP system. Use
+ * it when you want to count things and do not read the counter often. Ideally you
+ * should wait until probe exit time to read the Counter.
+ * @{
+ */
+
+/* This define will probably go away with the next checkin. */
+/* locks are only here for testing */
+#ifndef NEED_COUNTER_LOCKS
+#define NEED_COUNTER_LOCKS 0
+#endif
+
+#if NEED_COUNTER_LOCKS == 1
+#define COUNTER_LOCK(c) spin_lock(&c->lock)
+#define COUNTER_UNLOCK(c) spin_unlock(&c->lock)
+#else
+#define COUNTER_LOCK(c) ;
+#define COUNTER_UNLOCK(c) ;
+#endif
+
+struct _counter {
+ int64_t count;
+#if NEED_COUNTER_LOCKS == 1
+ spinlock_t lock;
+#endif
+};
+
+typedef struct _counter *Counter;
+
+
+/** Initialize a Counter.
+ * Call this during probe initialization to create a Counter
+ *
+ * @return a Counter. Will be NULL on error.
+ */
+Counter _stp_counter_init (void)
+{
+ Counter cnt = alloc_percpu (struct _counter);
+#if NEED_COUNTER_LOCKS == 1
+ {
+ int i;
+ for_each_cpu(i) {
+ Counter c = per_cpu_ptr (cnt, i);
+ c->lock = SPIN_LOCK_UNLOCKED;
+ }
+ }
+#endif
+ return cnt;
+}
+
+/** Add to a Counter.
+ * Adds an int64 to a Counter
+ *
+ * @param cnt Counter
+ * @param val int64 value
+ */
+void _stp_counter_add (Counter cnt, int64_t val)
+{
+ Counter c = per_cpu_ptr (cnt, get_cpu());
+ COUNTER_LOCK(c);
+ c->count += val;
+ COUNTER_UNLOCK(c);
+ put_cpu();
+}
+
+/** Get a Counter's per-cpu value.
+ * Get the value of a Counter for a specific CPU.
+ *
+ * @param cnt Counter
+ * @param cpu CPU number
+ * @param clear Set this to have the value cleared after reading.
+ * @return An int64 value.
+ */
+int64_t _stp_counter_get_cpu (Counter cnt, int cpu, int clear)
+{
+ int64_t val;
+ Counter c = per_cpu_ptr (cnt, cpu);
+ COUNTER_LOCK(c);
+ val = c->count;
+ if (clear)
+ c->count = 0;
+ COUNTER_UNLOCK(c);
+ return val;
+}
+
+/** Get a Counter's value.
+ * Get the value of a Counter. This is the sum of the counters for
+ * all CPUs. Because computing this sum requires reading all of the
+ * per-cpu values, doing it often will result in poor performance in
+ * multiprocessor systems.
+ *
+ * The clear parameter is intended for use in a polling situation when the
+ * values should be immediately cleared after reading.
+ * @param cnt Counter
+ * @param clear Set this to have the value cleared after reading.
+ * @return An int64 value.
+ */
+int64_t _stp_counter_get (Counter cnt, int clear)
+{
+ int i;
+ int64_t sum = 0;
+
+ for_each_cpu(i) {
+ Counter c = per_cpu_ptr (cnt, i);
+ COUNTER_LOCK(c);
+ sum += c->count;
+ if (clear)
+ c->count = 0;
+ COUNTER_UNLOCK(c);
+ }
+ return sum;
+}
+
+/** Free a Counter.
+ * @param cnt Counter
+ */
+void _stp_counter_free (Counter cnt)
+{
+ free_percpu (cnt);
+}
+
+/** @} */
+
+#undef COUNTER_LOCK
+#undef COUNTER_UNLOCK
+#endif /* _COUNTER_C_ */
+
diff --git a/runtime/docs/examples/argv.c b/runtime/docs/examples/argv.c
index 609ad74f..0acd41e0 100644
--- a/runtime/docs/examples/argv.c
+++ b/runtime/docs/examples/argv.c
@@ -8,6 +8,7 @@ int inst_do_execve (char * filename, char __user *__user *argv, char __user *__u
_stp_copy_argv_from_user (arglist, argv);
foreach (arglist, ptr)
- printk ("%s ", ptr->str);
- printk ("\n");
+ _stp_printf ("%s ", _stp_get_str(ptr));
+ _stp_print("\n");
+
}
diff --git a/runtime/docs/examples/foreach.c b/runtime/docs/examples/foreach.c
index fc110674..8000d60b 100644
--- a/runtime/docs/examples/foreach.c
+++ b/runtime/docs/examples/foreach.c
@@ -1,9 +1,9 @@
/* example showing how to print all the stats in a map using foreach() */
-struct map_node_stat *ptr;
+ struct map_node *ptr;
+
+ foreach (mapsst, ptr)
+ _stp_printf ("mapsst[%09s,%09s] = %llX\n", key1str(ptr), key2str(ptr), _stp_get_stat(ptr)->sum);
+
-foreach (map, ptr)
- printf ("map[%s,%ld] = [c=%lld s=%lld min=%lld max=%lld]\n", key1str(ptr),
- key2int(ptr), ptr->stats.count, ptr->stats.sum, ptr->stats.min,
- ptr->stats.max);
diff --git a/runtime/docs/examples/list.c b/runtime/docs/examples/list.c
index 15d13d4b..6294cccb 100644
--- a/runtime/docs/examples/list.c
+++ b/runtime/docs/examples/list.c
@@ -1,5 +1,5 @@
-struct map_node_str *ptr;
+struct map_node *ptr;
MAP map = _stp_list_new(10, STRING);
@@ -8,7 +8,10 @@ for (i = 0; i < 10; i++) {
_stp_list_add (map, buf);
}
+/* old way to print a list of strings */
foreach (map, ptr)
- printf ("map[%ld] = %s\n", key1int(ptr), ptr->str);
+ _stp_printf ("list[%ld] = %s\n", key1int(ptr), _stp_get_str(ptr));
+/* new way to print a list of strings */
+ _stp_map_print(map, "list[%1d] = %s");
diff --git a/runtime/docs/examples/map.c b/runtime/docs/examples/map.c
index ca661d05..f5f6f19f 100644
--- a/runtime/docs/examples/map.c
+++ b/runtime/docs/examples/map.c
@@ -1,6 +1,6 @@
/* create a map with a max of 100 elements */
-MAP mymap = map_new(100, INT64);
+MAP mymap = _stp_map_new(100, INT64);
/* mymap[birth year] = 2000 */
map_key_str (mymap, "birth year");
diff --git a/runtime/io.c b/runtime/io.c
index 7c8c5ac0..bc691ebf 100644
--- a/runtime/io.c
+++ b/runtime/io.c
@@ -2,7 +2,6 @@
#define _IO_C_
#include "transport/transport.c"
-#include "print.c"
/** @file io.c
* @brief I/O functions
@@ -28,7 +27,7 @@ static char _stp_lbuf[NR_CPUS][STP_LOG_BUF_LEN + 1];
void _stp_log (const char *fmt, ...)
{
int num, ret;
- char *buf = &_stp_lbuf[smp_processor_id()][0];
+ char *buf = &_stp_lbuf[get_cpu()][0];
va_list args;
va_start(args, fmt);
num = vscnprintf (buf, STP_LOG_BUF_LEN, fmt, args);
@@ -38,6 +37,7 @@ void _stp_log (const char *fmt, ...)
ret = _stp_ctrl_send(STP_REALTIME_DATA, buf, num + 1, t->pid);
if (ret < 0)
atomic_inc (&_stp_transport_failures);
+ put_cpu();
}
/** @} */
diff --git a/runtime/list.c b/runtime/list.c
index 2ab3f2ea..fb5e670b 100644
--- a/runtime/list.c
+++ b/runtime/list.c
@@ -12,6 +12,9 @@
#include "map.c"
/********************** List Functions *********************/
+/** @file list.c
+ * @brief List Functions
+ */
/** @addtogroup lists
* Lists are special cases of maps.
diff --git a/runtime/map-int.c b/runtime/map-int.c
new file mode 100644
index 00000000..fd362eb8
--- /dev/null
+++ b/runtime/map-int.c
@@ -0,0 +1,73 @@
+/* -*- linux-c -*- */
+
+/** @file map-int.c
+ * @brief Map functions to set and get int64s
+ */
+
+void __stp_map_set_int64 (MAP map, int64_t val, int add)
+{
+ struct map_node *m;
+
+ if (map == NULL)
+ return;
+
+ if (map->create) {
+ if (val == 0 && !map->no_wrap)
+ return;
+
+ m = __stp_map_create (map);
+ if (!m)
+ return;
+
+ /* set the value */
+ dbug ("m=%lx offset=%lx\n", (long)m, (long)map->data_offset);
+ *(int64_t *)((long)m + map->data_offset) = val;
+ } else {
+ if (map->key == NULL)
+ return;
+
+ if (val) {
+ if (add)
+ *(int64_t *)((long)map->key + map->data_offset) += val;
+ else
+ *(int64_t *)((long)map->key + map->data_offset) = val;
+ } else if (!add) {
+ /* setting value to 0 is the same as deleting */
+ _stp_map_key_del(map);
+ }
+ }
+}
+/** Set the current element's value to an int64.
+ * This sets the current element's value to an int64. The map must have been created
+ * to hold int64s using <i>_stp_map_new_(xxx, INT64)</i>
+ *
+ * If the element doesn't exist, it is created. If no current element (key)
+ * is set for the map, this function does nothing.
+ * @param map
+ * @param val new value
+ * @sa _stp_map_add_int64()
+ * @sa _stp_map_set()
+ * @ingroup map_set
+ */
+#define _stp_map_set_int64(map,val) __stp_map_set_int64 (map,val,0)
+
+/** Get the value of a map.
+ * This gets the current element's int64 value. The map must have been created
+ * to hold int64s using <i>_stp_map_new_(xxx, INT64)</i>
+ *
+ * If no current element (key) is set for the map, this function returns 0.
+ *
+ * @ingroup map_set
+ * @param map
+ * @returns an int64 value.
+ */
+int64_t _stp_map_get_int64 (MAP map)
+{
+ struct map_node *m;
+ if (map == NULL || map->create || map->key == NULL)
+ return 0;
+ dbug ("key %lx\n", (long)map->key);
+ m = (struct map_node *)map->key;
+ return *(int64_t *)((long)m + map->data_offset);
+}
+
diff --git a/runtime/map-keys.c b/runtime/map-keys.c
index 894d79f5..1dd78f30 100644
--- a/runtime/map-keys.c
+++ b/runtime/map-keys.c
@@ -1,4 +1,9 @@
/* -*- linux-c -*- */
+/** @file map-keys.c
+ * @brief Map functions to set and get keys
+ * This file is a template designed to be included as many times as
+ * needed to generate the necessary functions to set map keys.
+ */
#include "map.h"
@@ -302,6 +307,7 @@ MAP KEYSYM(_stp_map_new) (unsigned max_entries, int valtype, ...)
m = _stp_map_new (max_entries, valtype & 0x0f,
sizeof(struct KEYSYM(map_node)), 0);
break;
+#ifdef NEED_STAT_VALS
case HIST_LOG:
m = _stp_map_new_hstat_log (max_entries, sizeof(struct KEYSYM(map_node)),
buckets);
@@ -310,6 +316,7 @@ MAP KEYSYM(_stp_map_new) (unsigned max_entries, int valtype, ...)
m = _stp_map_new_hstat_linear (max_entries, sizeof(struct KEYSYM(map_node)),
start, stop, interval);
break;
+#endif
default:
dbug ("ERROR: unknown histogram type %d\n", htype);
m = NULL;
@@ -409,3 +416,5 @@ void KEYSYM(_stp_map_key) (MAP map, ALLKEYSD(key))
#undef KEYSYM
#undef ALLKEYS
#undef ALLKEYSD
+
+#include "map-values.c"
diff --git a/runtime/map-stat.c b/runtime/map-stat.c
new file mode 100644
index 00000000..a98f8b87
--- /dev/null
+++ b/runtime/map-stat.c
@@ -0,0 +1,84 @@
+/* -*- linux-c -*- */
+
+/** @file map-stat.c
+ * @brief Map functions to set and get stats
+ */
+
+#include "stat-common.c"
+
+/* Adds an int64 to a stats map */
+static void _stp_map_add_stat (MAP map, int64_t val)
+{
+ stat *d;
+ Stat st;
+
+ if (map == NULL)
+ return;
+
+ if (map->create) {
+ struct map_node *m = __stp_map_create (map);
+ if (!m)
+ return;
+
+ /* set the value */
+ d = (stat *)((long)m + map->data_offset);
+ d->count = 0;
+ } else {
+ if (map->key == NULL)
+ return;
+ d = (stat *)((long)map->key + map->data_offset);
+ }
+ st = (Stat)((long)map + offsetof(struct map_root, hist_type));
+ __stp_stat_add (st, d, val);
+}
+
+
+static void _stp_map_print_histogram (MAP map, stat *sd)
+{
+ Stat st = (Stat)((long)map + offsetof(struct map_root, hist_type));
+ _stp_stat_print_histogram (st, sd);
+}
+
+static MAP _stp_map_new_hstat_log (unsigned max_entries, int key_size, int buckets)
+{
+ /* add size for buckets */
+ int size = buckets * sizeof(int64_t) + sizeof(stat);
+ MAP m = _stp_map_new (max_entries, STAT, key_size, size);
+ if (m) {
+ m->hist_type = HIST_LOG;
+ m->hist_buckets = buckets;
+ if (buckets < 1 || buckets > 64) {
+ dbug ("histogram: Bad number of buckets. Must be between 1 and 64\n");
+ m->hist_type = HIST_NONE;
+ return m;
+ }
+ }
+ return m;
+}
+
+static MAP _stp_map_new_hstat_linear (unsigned max_entries, int ksize, int start, int stop, int interval)
+{
+ MAP m;
+ int size;
+ int buckets = (stop - start) / interval;
+ if ((stop - start) % interval) buckets++;
+
+ /* add size for buckets */
+ size = buckets * sizeof(int64_t) + sizeof(stat);
+
+ m = _stp_map_new (max_entries, STAT, ksize, size);
+ if (m) {
+ m->hist_type = HIST_LINEAR;
+ m->hist_start = start;
+ m->hist_stop = stop;
+ m->hist_int = interval;
+ m->hist_buckets = buckets;
+ if (m->hist_buckets <= 0) {
+ dbug ("histogram: bad stop, start and/or interval\n");
+ m->hist_type = HIST_NONE;
+ return m;
+ }
+
+ }
+ return m;
+}
diff --git a/runtime/map-str.c b/runtime/map-str.c
new file mode 100644
index 00000000..7cdf842c
--- /dev/null
+++ b/runtime/map-str.c
@@ -0,0 +1,118 @@
+/* -*- linux-c -*- */
+
+/** @file map-str.c
+ * @brief Map functions to set and get strings
+ */
+
+/* from map.c */
+void str_copy(char *dest, char *src);
+
+void str_add(void *dest, char *val)
+{
+ char *dst = (char *)dest;
+ int len = strlen(val);
+ int len1 = strlen(dst);
+ int num = MAP_STRING_LENGTH - 1 - len1;
+
+ if (len > num)
+ len = num;
+ strncpy (&dst[len1], val, len);
+ dst[len + len1] = 0;
+}
+
+void __stp_map_set_str (MAP map, char *val, int add)
+{
+ struct map_node *m;
+
+ if (map == NULL)
+ return;
+
+ if (map->create) {
+ if (val == 0 && !map->no_wrap)
+ return;
+
+ m = __stp_map_create (map);
+ if (!m)
+ return;
+
+ /* set the value */
+ dbug ("m=%lx offset=%lx\n", (long)m, (long)map->data_offset);
+ str_copy((void *)((long)m + map->data_offset), val);
+ } else {
+ if (map->key == NULL)
+ return;
+
+ if (val) {
+ if (add)
+ str_add((void *)((long)map->key + map->data_offset), val);
+ else
+ str_copy((void *)((long)map->key + map->data_offset), val);
+ } else if (!add) {
+ /* setting value to 0 is the same as deleting */
+ _stp_map_key_del(map);
+ }
+ }
+}
+
+/** Set the current element's value to a string.
+ * This sets the current element's value to a string. The map must have been created
+ * to hold strings using <i>_stp_map_new(xxx, STRING)</i>
+ *
+ * If the element doesn't exist, it is created. If no current element (key)
+ * is set for the map, this function does nothing.
+ * @param map
+ * @param str String containing new value.
+ * @sa _stp_map_set()
+ * @ingroup map_set
+ */
+#define _stp_map_set_str(map,val) __stp_map_set_str(map,val,0)
+/** Add to the current element's string value.
+ * This sets the current element's value to a string consisting of the old
+ * contents followed by the new string. The map must have been created
+ * to hold strings using <i>_stp_map_new(xxx, STRING)</i>
+ *
+ * If the element doesn't exist, it is created. If no current element (key)
+ * is set for the map, this function does nothing.
+ * @param map
+ * @param str String containing value to append.
+ * @ingroup map_set
+ */
+#define _stp_map_add_str(map,val) __stp_map_set_str(map,val,1)
+
+/** Get the current element's string value.
+ * This gets the current element's string value. The map must have been created
+ * to hold strings using <i>_stp_map_new(xxx, STRING)</i>
+ *
+ * If no current element (key) is set for the map, this function
+ * returns NULL.
+ * @param map
+ * @sa _stp_map_set()
+ * @ingroup map_set
+ */
+char *_stp_map_get_str (MAP map)
+{
+ struct map_node *m;
+ if (map == NULL || map->create || map->key == NULL)
+ return 0;
+ dbug ("key %lx\n", (long)map->key);
+ m = (struct map_node *)map->key;
+ return (char *)((long)m + map->data_offset);
+}
+
+/** Set the current element's value to String.
+ * This sets the current element's value to a String. The map must have been created
+ * to hold strings using <i>_stp_map_new(xxx, STRING)</i>
+ *
+ * If the element doesn't exist, it is created. If no current element (key)
+ * is set for the map, this function does nothing.
+ * @param map
+ * @param str String containing new value.
+ * @sa _stp_map_set()
+ * @ingroup map_set
+ */
+
+void _stp_map_set_string (MAP map, String str)
+{
+ __stp_map_set_str (map, str->buf, 0);
+}
+
diff --git a/runtime/map-values.c b/runtime/map-values.c
index 6f8adf11..9adc9789 100644
--- a/runtime/map-values.c
+++ b/runtime/map-values.c
@@ -1,138 +1,50 @@
-/* -*- linux-c -*- */
+#ifndef _MAP_VALUES_C_ /* -*- linux-c -*- */
+#define _MAP_VALUES_C_
+
+/** @file map-values.c
+ * @brief Includes the proper value functions for maps.
+ */
#include "map.h"
-#if VALUE_TYPE == STRING
-#define VALUETYPE char*
-#define VALUENAME str
-#define NEED_STRING_VALS
-#elif VALUE_TYPE == INT64
-#define VALUETYPE int64_t
-#define VALUENAME int64
-#define NEED_INT64_VALS
-#elif VALUE_TYPE == STAT
-#define VALUETYPE stat*
-#define VALUENAME stat
-#define NEED_STAT_VALS
-#else
-#error VALUE_TYPE has unimplemented value.
+#if !defined(NEED_STRING_VALS) && !defined(NEED_INT64_VALS) && !defined(NEED_STAT_VALS)
+#error Need to define at least one of NEED_STRING_VALS, NEED_INT64_VALS and NEED_STAT_VALS
#endif
-#define VALSYM(x) JOIN(x,VALUENAME)
-#define VALUE_TYPE_COPY JOIN(VALUENAME,copy)
-#define VALUE_TYPE_ADD JOIN(VALUENAME,add)
-#define VALUE_GET JOIN(VALUENAME,get)
-
-void VALSYM(__stp_map_set) (MAP map, VALUETYPE val, int add)
-{
- struct map_node *m;
-
- if (map == NULL)
- return;
-
- if (map->create) {
- if (val == 0 && !map->no_wrap)
- return;
+#ifdef NEED_STRING_VALS
+#include "map-str.c"
+#endif
- m = __stp_map_create (map);
- if (!m)
- return;
-
- /* set the value */
- dbug ("m=%lx offset=%lx\n", (long)m, (long)map->data_offset);
- VALUE_TYPE_COPY((void *)((long)m + map->data_offset), val);
- //m->val = val;
- } else {
- if (map->key == NULL)
- return;
-
- if (val) {
- if (add)
- VALUE_TYPE_ADD((void *)((long)map->key + map->data_offset), val);
- else
- VALUE_TYPE_COPY((void *)((long)map->key + map->data_offset), val);
- } else if (!add) {
- /* setting value to 0 is the same as deleting */
- _stp_map_key_del(map);
- }
- }
-}
+#ifdef NEED_STAT_VALS
+#include "map-stat.c"
+#endif
-void VALSYM(_stp_map_set) (MAP map, VALUETYPE val)
-{
- VALSYM(__stp_map_set)(map, val, 0);
-}
+#ifdef NEED_INT64_VALS
+#include "map-int.c"
+#endif
-#if VALUE_TYPE == STAT
-/** Adds an int64 to a stats map */
-void VALSYM(_stp_map_add_int64) (MAP map, int64_t val)
+#if defined(NEED_INT64_VALS) || defined (NEED_STAT_VALS)
+/** Add an int64 to a map.
+ * @ingroup map_set
+ * @param map
+ * @param val int64 value to add
+ */
+void _stp_map_add_int64 (MAP map, int64_t val)
{
- stat *d;
- int n;
-
if (map == NULL)
return;
-
- if (map->create) {
- struct map_node *m = __stp_map_create (map);
- if (!m)
- return;
-
- /* set the value */
- d = (stat *)((long)m + map->data_offset);
- d->count = 1;
- d->sum = d->min = d->max = val;
- } else {
- if (map->key == NULL)
- return;
- d = (stat *)((long)map->key + map->data_offset);
- d->count++;
- d->sum += val;
- if (val > d->max)
- d->max = val;
- if (val < d->min)
- d->min = val;
- }
- /* histogram */
- switch (map->hist_type) {
- case HIST_LOG:
- n = msb64 (val);
- if (n >= map->hist_buckets)
- n = map->hist_buckets - 1;
- d->histogram[n]++;
- break;
- case HIST_LINEAR:
- /* n = (val - map->hist_start) / map->hist_int; */
- val -= map->hist_start;
- do_div (val, map->hist_int);
- n = val;
- if (n < 0)
- n = 0;
- if (n >= map->hist_buckets)
- n = map->hist_buckets - 1;
- d->histogram[n]++;
- default:
- break;
- }
-}
-#endif /* VALUE_TYPE == STAT */
-void VALSYM(_stp_map_add) (MAP map, VALUETYPE val)
-{
- VALSYM(__stp_map_set)(map, val, 1);
+#ifdef NEED_INT64_VALS
+ if (map->type == INT64)
+ __stp_map_set_int64 (map, val, 1);
+#endif
+#ifdef NEED_STAT_VALS
+ if (map->type == STAT)
+ _stp_map_add_stat (map, val);
+#endif
}
+#endif
-VALUETYPE VALSYM(_stp_map_get) (MAP map)
-{
- struct map_node *m;
- if (map == NULL || map->create || map->key == NULL)
- return 0;
- dbug ("key %lx\n", (long)map->key);
- m = (struct map_node *)map->key;
- return VALUE_GET ((void *)((long)m + map->data_offset));
-}
+#endif /* _MAP_VALUES_C_ */
-#undef VALUE_TYPE
-#undef VALUETYPE
-#undef VALUENAME
diff --git a/runtime/map.c b/runtime/map.c
index 98ed6af9..187a1872 100644
--- a/runtime/map.c
+++ b/runtime/map.c
@@ -28,120 +28,8 @@ int int64_eq_p (int64_t key1, int64_t key2)
#endif /* NEED_INT64_KEYS */
-#ifdef NEED_INT64_VALS
-void int64_copy (void *dest, int64_t val)
-{
- *(int64_t *)dest = val;
-}
-
-void int64_add (void *dest, int64_t val)
-{
- *(int64_t *)dest += val;
-}
-
-int64_t int64_get (void *ptr)
-{
- return *(int64_t *)ptr;
-}
-#endif /* NEED_INT64_VALS */
-
-#ifdef NEED_STAT_VALS
-void stat_copy (void *dest, stat *src)
-{
- memcpy (dest, src, sizeof(stat));
-}
-
-void stat_add (void *dest, stat *src)
-{
- stat *d = (stat *)dest;
-
- d->count =+ src->count;
- d->sum += src->sum;
- if (src->max > d->max)
- d->max = src->max;
- if (src->min < d->min)
- d->min = src->min;
- /* FIXME: do histogram */
-}
-
-stat *stat_get(void *ptr)
-{
- return (stat *)ptr;
-}
-
-/* implements a log base 2 function, or Most Significant Bit */
-/* with bits from 1 (lsb) to 64 (msb) */
-/* msb64(0) = 0 */
-/* msb64(1) = 1 */
-/* msb64(8) = 4 */
-/* msb64(512) = 10 */
-
-int msb64(int64_t val)
-{
- int res = 64;
-
- if (val == 0)
- return 0;
-
- /* shortcut. most values will be 16-bit */
- if (val & 0xffffffffffff0000ull) {
- if (!(val & 0xffffffff00000000ull)) {
- val <<= 32;
- res -= 32;
- }
-
- if (!(val & 0xffff000000000000ull)) {
- val <<= 16;
- res -= 16;
- }
- } else {
- val <<= 48;
- res -= 48;
- }
-
- if (!(val & 0xff00000000000000ull)) {
- val <<= 8;
- res -= 8;
- }
-
- if (!(val & 0xf000000000000000ull)) {
- val <<= 4;
- res -= 4;
- }
-
- if (!(val & 0xc000000000000000ull)) {
- val <<= 2;
- res -= 2;
- }
-
- if (!(val & 0x8000000000000000ull)) {
- val <<= 1;
- res -= 1;
- }
-
- return res;
-}
-
-#endif /* NEED_STAT_VALS */
-
-int64_t _stp_key_get_int64 (struct map_node *mn, int n)
-{
- if (mn)
- return (*mn->map->get_key)(mn, n, NULL).val;
- return 0;
-}
-
-char *_stp_key_get_str (struct map_node *mn, int n)
-{
- if (mn)
- return (*mn->map->get_key)(mn, n, NULL).strp;
- return "";
-}
-
-
-
-#if defined(NEED_STRING_VALS) || defined (NEED_STRING_KEYS)
+#if defined (NEED_STRING_KEYS) || defined (NEED_STRING_VALS)
void str_copy(char *dest, char *src)
{
int len = strlen(src);
@@ -152,43 +40,6 @@ void str_copy(char *dest, char *src)
}
#endif
-#ifdef NEED_STRING_VALS
-void str_add(void *dest, char *val)
-{
- char *dst = (char *)dest;
- int len = strlen(val);
- int len1 = strlen(dst);
- int num = MAP_STRING_LENGTH - 1 - len1;
-
- if (len > num)
- len = num;
- strncpy (&dst[len1], val, len);
- dst[len + len1] = 0;
-}
-
-char *str_get (void *ptr)
-{
- return ptr;
-}
-
-/** Set the current element's value to String.
- * This sets the current element's value to a String. The map must have been created
- * to hold int64s using <i>_stp_map_new(xxx, STRING)</i>
- *
- * If the element doesn't exist, it is created. If no current element (key)
- * is set for the map, this function does nothing.
- * @param map
- * @param str String containing new value.
- * @sa _stp_map_set()
- */
-
-void _stp_map_set_string (MAP map, String str)
-{
- _stp_map_set_str (map, str->buf);
-}
-
-#endif /* NEED_STRING_VALS */
-
#ifdef NEED_STRING_KEYS
int str_eq_p (char *key1, char *key2)
{
@@ -206,26 +57,74 @@ unsigned int str_hash(const char *key1)
}
#endif /* NEED_STRING_KEYS */
+/** @addtogroup maps
+ * Implements maps (associative arrays) and lists
+ * @{
+ */
+
+/** Return an int64 from a map node.
+ * This function will return the int64 value of a map_node
+ * from a map containing int64s. You can get the map_nodes in a map
+ * with _stp_map_start(), _stp_map_iter() and foreach().
+ * @param m pointer to the map_node.
+ * @returns an int64 value.
+ */
int64_t _stp_get_int64(struct map_node *m)
{
return *(int64_t *)((long)m + m->map->data_offset);
}
+/** Return a string from a map node.
+ * This function will return the string value of a map_node
+ * from a map containing strings. You can get the map_nodes in a map
+ * with _stp_map_start(), _stp_map_iter() and foreach().
+ * @param m pointer to the map_node.
+ * @returns a pointer to a string.
+ */
char *_stp_get_str(struct map_node *m)
{
return (char *)((long)m + m->map->data_offset);
}
+/** Return a stat pointer from a map node.
+ * This function will return the stats of a map_node
+ * from a map containing stats. You can get the map_nodes in a map
+ * with _stp_map_start(), _stp_map_iter() and foreach().
+ * @param m pointer to the map_node.
+ * @returns A pointer to the stats.
+ */
stat *_stp_get_stat(struct map_node *m)
{
return (stat *)((long)m + m->map->data_offset);
}
+/** Return an int64 key from a map node.
+ * This function will return an int64 key from a map_node.
+ * @param m pointer to the map_node.
+ * @param n key number
+ * @returns an int64
+ * @sa key1int(), key2int()
+ */
+int64_t _stp_key_get_int64 (struct map_node *mn, int n)
+{
+ if (mn)
+ return (*mn->map->get_key)(mn, n, NULL).val;
+ return 0;
+}
-/** @addtogroup maps
- * Implements maps (associative arrays) and lists
- * @{
+/** Return a string key from a map node.
+ * This function will return an string key from a map_node.
+ * @param m pointer to the map_node.
+ * @param n key number
+ * @returns a pointer to a string
+ * @sa key1str(), key2str()
*/
+char *_stp_key_get_str (struct map_node *mn, int n)
+{
+ if (mn)
+ return (*mn->map->get_key)(mn, n, NULL).strp;
+ return "";
+}
/** Create a new map.
* Maps must be created at module initialization time.
@@ -235,6 +134,7 @@ stat *_stp_get_stat(struct map_node *m)
* will be allocated dynamically.
* @param type Type of values stored in this map.
* @return A MAP on success or NULL on failure.
+ * @ingroup map_create
*/
static MAP _stp_map_new(unsigned max_entries, int type, int key_size, int data_size)
@@ -283,49 +183,6 @@ static MAP _stp_map_new(unsigned max_entries, int type, int key_size, int data_s
return m;
}
-MAP _stp_map_new_hstat_log (unsigned max_entries, int key_size, int buckets)
-{
- /* add size for buckets */
- int size = buckets * sizeof(int64_t) + sizeof(stat);
- MAP m = _stp_map_new (max_entries, STAT, key_size, size);
- if (m) {
- m->hist_type = HIST_LOG;
- m->hist_buckets = buckets;
- if (buckets < 1 || buckets > 64) {
- dbug ("histogram: Bad number of buckets. Must be between 1 and 64\n");
- m->hist_type = HIST_NONE;
- return m;
- }
- }
- return m;
-}
-
-MAP _stp_map_new_hstat_linear (unsigned max_entries, int ksize, int start, int stop, int interval)
-{
- MAP m;
- int size;
- int buckets = (stop - start) / interval;
- if ((stop - start) % interval) buckets++;
-
- /* add size for buckets */
- size = buckets * sizeof(int64_t) + sizeof(stat);
-
- m = _stp_map_new (max_entries, STAT, ksize, size);
- if (m) {
- m->hist_type = HIST_LINEAR;
- m->hist_start = start;
- m->hist_stop = stop;
- m->hist_int = interval;
- m->hist_buckets = buckets;
- if (m->hist_buckets <= 0) {
- dbug ("histogram: bad stop, start and/or interval\n");
- m->hist_type = HIST_NONE;
- return m;
- }
-
- }
- return m;
-}
/** Deletes the current element.
* If no current element (key) for this map is set, this function does nothing.
@@ -421,150 +278,6 @@ void _stp_map_del(MAP map)
_stp_vfree(map);
}
-#ifdef NEED_STAT_VALS
-
-static int needed_space(int64_t v)
-{
- int space = 0;
-
- if (v == 0)
- return 1;
-
- if (v < 0) {
- space++;
- v = -v;
- }
- while (v) {
- /* v /= 10; */
- do_div (v, 10);
- space++;
- }
- return space;
-}
-
-static void reprint (int num, char *s)
-{
- while (num > 0) {
- _stp_print_cstr (s);
- num--;
- }
-}
-
-#define HIST_WIDTH 50
-
-void _stp_map_print_histogram (MAP map, stat *s)
-{
- int scale, i, j, val_space, cnt_space;
- int64_t val, v, max = 0;
-
- if (map->hist_type != HIST_LOG && map->hist_type != HIST_LINEAR)
- return;
- /* get the maximum value, for scaling */
-
- for (i = 0; i < map->hist_buckets; i++)
- if (s->histogram[i] > max)
- max = s->histogram[i];
-
- if (max <= HIST_WIDTH)
- scale = 1;
- else {
- int64_t tmp = max;
- int rem = do_div (tmp, HIST_WIDTH);
- scale = tmp;
- if (rem) scale++;
- }
-
- cnt_space = needed_space (max);
- if (map->hist_type == HIST_LINEAR)
- val_space = needed_space (map->hist_start + map->hist_int * (map->hist_buckets - 1));
- else
- val_space = needed_space (1 << (map->hist_buckets - 1));
- dbug ("max=%lld scale=%d val_space=%d\n", max, scale, val_space);
-
- /* print header */
- j = 0;
- if (val_space > 5) /* 5 = sizeof("value") */
- j = val_space - 5;
- else
- val_space = 5;
- for ( i = 0; i < j; i++)
- _stp_print_cstr (" ");
- _stp_print_cstr("value |");
- reprint (HIST_WIDTH, "-");
- _stp_print_cstr (" count\n");
- _stp_print_flush();
- if (map->hist_type == HIST_LINEAR)
- val = map->hist_start;
- else
- val = 0;
- for (i = 0; i < map->hist_buckets; i++) {
- reprint (val_space - needed_space(val), " ");
- _stp_printf("%d", val);
- _stp_print_cstr (" |");
-
- /* v = s->histogram[i] / scale; */
- v = s->histogram[i];
- do_div (v, scale);
-
- reprint (v, "@");
- reprint (HIST_WIDTH - v + 1 + cnt_space - needed_space(s->histogram[i]), " ");
- _stp_printf ("%lld\n", s->histogram[i]);
- if (map->hist_type == HIST_LINEAR)
- val += map->hist_int;
- else if (val == 0)
- val = 1;
- else
- val *= 2;
- }
-}
-#endif /* NEED_STAT_VALS */
-
-/* Print stuff until a format specification is found. */
-/* Return pointer to that. */
-static char *next_fmt(char *fmt, int *num)
-{
- char *f = fmt;
- int in_fmt = 0;
- dbug ("next_fmt %s\n", fmt);
- *num = 0;
- while (*f) {
- if (in_fmt) {
- if (*f == '%') {
- _stp_string_cat_char(_stp_stdout,'%');
- in_fmt = 0;
- } else if (*f > '0' && *f <= '9') {
- *num = *f - '0';
- f++;
- return f;
- } else
- return f;
- } else if (*f == '%')
- in_fmt = 1;
- else
- _stp_string_cat_char(_stp_stdout,*f);
- f++;
- }
- return f;
-}
-
-/* print type based on format. Valid formats are:
---- KEYS and RESULTS ---
-%p - address (hex padded to sizeof(void *))
-%P - symbolic address
-%x - hex int64
-%X - HEX int64
-%d - decimal int64
-%s - string
---- STATS ---
-%m - min
-%M - max
-%A - avg
-%S - sum
-%H - histogram
-%C - count
---- MISC ---
-%% - print '%'
-*/
static int print_keytype (char *fmt, int type, key_data *kd)
{
dbug ("*fmt = %c\n", *fmt);
@@ -628,41 +341,23 @@ static void print_valtype (MAP map, char *fmt, struct map_node *ptr)
#ifdef NEED_STAT_VALS
case STAT:
{
- stat *s = _stp_get_stat(ptr);
- switch (*fmt) {
- case 'C':
- _stp_printf("%lld", s->count);
- break;
- case 'm':
- _stp_printf("%lld", s->min);
- break;
- case 'M':
- _stp_printf("%lld", s->max);
- break;
- case 'S':
- _stp_printf("%lld", s->sum);
- break;
- case 'A':
- {
- int64_t avg = s->sum;
- do_div (avg, (int)s->count); /* FIXME: check for overflow */
- _stp_printf("%lld", avg);
- break;
- }
- case 'H':
- _stp_map_print_histogram (map, s);
- _stp_print_flush();
- break;
- }
+ Stat st = (Stat)((long)map + offsetof(struct map_root, hist_type));
+ stat *sd = _stp_get_stat(ptr);
+ _stp_stat_print_valtype (fmt, st, sd, 0);
break;
}
-
#endif
default:
break;
}
}
+/** Print a Map.
+ * Print a Map using a format string.
+ *
+ * @param map Map
+ * @param fmt @ref format_string
+ */
void _stp_map_print (MAP map, const char *fmt)
{
struct map_node *ptr;
diff --git a/runtime/map.doc b/runtime/map.doc
index 94d9ffdd..6a1585b7 100644
--- a/runtime/map.doc
+++ b/runtime/map.doc
@@ -1,7 +1,48 @@
-/* This is for documentation only. Do not compile. */
-/* -*- linux-c -*- */
-
+/** @addtogroup maps Maps (Associative Arrays)
+
+Maps are implemented as hash lists. In order to efficiently handle maps with up to five keys of any combination
+of int64s and strings, the functions to create maps, set keys, and set and get values
+are all generated as needed. To do this, you must simply do some defines and
+include the proper files. For example, if you need a map with 3 keys, int64, int64, and string
+that stores strings, you need to do
+@code
+
+#define NEED_STRING_VALS
+
+#define KEY1_TYPE INT64
+#define KEY2_TYPE INT64
+#define KEY3_TYPE STRING
+#include "map-keys.c"
+
+#include "map.c"
+@endcode
+
+In the above example, maps are created with _stp_map_new_int64_int64_str(). All the
+generated functions start with a base name (such as _stp_map_new) and add the key types separated
+by underscores.
+
+Each map can hold only one type of data; int64, string, or statistics. Each element belonging to a map can have up to
+5 keys and a value. Implemented key types are strings and int64.
+
+To simplify the implementation, the functions to set the key and the functions to set the data are separated.
+The map remembers what the current key is.
+For example, to continue our previous example,
+@code
+/* create a map with a max of 100 elements containing strings */
+MAP mymap = _stp_map_new_int64_int64_str(100, STRING);
+
+/* mymap[100, 300, "Ohio"] = "Columbus" */
+_stp_map_key_int64_int64_str (mymap, 100, 300, "Ohio");
+_stp_map_set_str (mymap, "Columbus");
+@endcode
+
+All elements have a default value of 0 (or NULL). Elements are only saved to the map when their value is set
+to something nonzero.
+
+For good examples, see the runtime/tests/maps directory or the example probes.
*/
+
+
/** @addtogroup maps
*
* @{
@@ -79,34 +120,6 @@ void _stp_map_set_str (MAP map, char *val) {}
*/
void _stp_map_set_string (MAP map, String str){}
-/** Set the current element's value to an int64.
- * This sets the current element's value to an int64. The map must have been created
- * to hold int64s using <i>_stp_map_new_(xxx, INT64)</i>
- *
- * If the element doesn't exist, it is created. If no current element (key)
- * is set for the map, this function does nothing.
- * @param map
- * @param val new value
- * @sa _stp_map_add_int64()
- * @sa _stp_map_set()
- */
-void _stp_map_set_int64 (MAP map, int64_t val) {}
-
-/** Set the current element's value to a stat.
- * This sets the current element's value to an stat struct. The map must have been created
- * to hold stats using <i>_stp_map_new_(xxx, STAT)</i> (or HSTAT_LOG or HSTAT_LINEAR). This function would only be used
- * if you want to set stats to something other than the normal initial values (count = 0,
- * sum = 0, etc).
- * @sa _stp_map_add_stat
- * @sa _stp_map_add_int64_stat
- *
- * If the element doesn't exist, it is created. If no current element (key)
- * is set for the map, this function does nothing.
- * @param map
- * @param val pointer to stats struct.
- */
-void _stp_map_set_stat (MAP map, stat *val) {}
-
/** Appends a C string to the current element's value
* This appends a C string to the current element's value. The map must have been created
* to hold strings using <i>_stp_map_new_(xxx, STRING)</i>
@@ -130,31 +143,6 @@ void _stp_map_add_str (MAP map, char *val) {}
* @sa _stp_map_set_int64()
*/
void _stp_map_add_int64 (MAP map, int64_t val) {}
-
-/** Add stats to the current element's value.
- * This adds stats to the current element's value to an stat struct. The map must have been created
- * to hold stats using <i>_stp_map_new_(xxx, STAT)</i> (or HSTAT_LOG or HSTAT_LINEAR). This function would only be used
- * to combine stats from multiple sources into a single stats struct.
- * @sa _stp_map_add_stat
- * @sa _stp_map_add_int64_stat
- *
- * If the element doesn't exist, it is created. If no current element (key)
- * is set for the map, this function does nothing.
- * @param map
- * @param val pointer to stats struct.
- */
-void _stp_map_add_stat (MAP map, stat *val) {}
-
-/** Add an int64 to the current element's statistics.
- * Increments the statistics counter by one and the sum by <i>val</i>.
- * Adjusts minimum, maximum, and histogram.
- *
- * If the element doesn't exist, it is created. If no current element (key)
- * is set for the map, this function does nothing.
- * @param map
- * @param val value to add to the statistics
- */
-void _stp_map_add_int64_stat (MAP map, int64_t val){}
/** @} end of map_set group*/
/** @defgroup map_get Getting the Values from Maps
@@ -189,3 +177,32 @@ int64_t _stp_map_get_int64 (MAP map) {}
/** @} end of map_get group */
/** @} */
+
+/** @page format_string Format Strings
+Format strings for maps and stats use a special format string. Be careful
+not to confuse it with a printf-style format.
+@verbatim
+--- KEYS and RESULTS ---
+%p - address (hex padded to sizeof(void *))
+%P - symbolic address
+%x - hex int64
+%X - HEX int64
+%d - decimal int64
+%s - string
+--- STATS ---
+%m - min
+%M - max
+%A - avg
+%S - sum
+%H - histogram
+%C - count
+--- MISC ---
+%% - print '%'
+--- PER_CPU ---
+%c - CPU number
+@endverbatim
+When printing map keys, format types are preceeded by a single-digit number
+indicating the key number. For example, "%1d,%2s" prints key 1 as an int64 followed by
+key 2 as a string. Here's a good example from the test files.
+@include map_format.c
+*/
diff --git a/runtime/map.h b/runtime/map.h
index 9660ce9b..2a31fa8e 100644
--- a/runtime/map.h
+++ b/runtime/map.h
@@ -9,14 +9,14 @@
* @{
*/
-/** This sets the size of the hash table. */
+/* This sets the size of the hash table. */
#ifndef HASH_TABLE_BITS
#define HASH_TABLE_BITS 8
-/** This sets the size of the hash table. */
+/* This sets the size of the hash table. */
#define HASH_TABLE_SIZE (1<<HASH_TABLE_BITS)
#endif
-/** The maximum number of keys allowed. Reducing this can save a small
+/* The maximum number of keys allowed. Reducing this can save a small
amount of memory. Do not increase above 5. */
#ifndef MAX_KEY_ARITY
#define MAX_KEY_ARITY 5
@@ -28,9 +28,6 @@ amount of memory. Do not increase above 5. */
#define MAP_STRING_LENGTH 256
#endif
-/** histogram type */
-enum histtype { HIST_NONE, HIST_LOG, HIST_LINEAR };
-
/** @cond DONT_INCLUDE */
#define INT64 0
#define STRING 1
@@ -38,21 +35,14 @@ enum histtype { HIST_NONE, HIST_LOG, HIST_LINEAR };
#define END 3
/** @endcond */
-/** Histogram is log 2 */
+#include "stat.h"
+
+/* Histogram is log 2 */
#define HSTAT_LOG (STAT | (HIST_LOG << 8))
-/** Histogram is linear */
+/* Histogram is linear */
#define HSTAT_LINEAR (STAT | (HIST_LINEAR << 8))
-/** Statistics are stored in this struct */
-typedef struct {
- int64_t count;
- int64_t sum;
- int64_t min, max;
- int64_t histogram[];
-} stat;
-
-
-/** Keys are either int64 or strings */
+/* Keys are either int64 or strings */
typedef union {
int64_t val;
char *strp;
@@ -163,6 +153,7 @@ typedef struct map_root *MAP;
/** Macro to call the proper _stp_map_set function based on the
* type of the argument.
+ * @ingroup map_set
*/
#define _stp_map_set(map, val) \
({ \
@@ -225,16 +216,18 @@ int str_eq_p(char *key1, char *key2);
int64_t _stp_get_int64(struct map_node *m);
char * _stp_get_str(struct map_node *m);
stat * _stp_get_stat(struct map_node *m);
-int msb64(int64_t x);
unsigned int str_hash(const char *key1);
static MAP _stp_map_new(unsigned max_entries, int type, int key_size, int data_size);
-MAP _stp_map_new_hstat_log(unsigned max_entries, int key_size, int buckets);
-MAP _stp_map_new_hstat_linear(unsigned max_entries, int ksize, int start, int stop, int interval);
+#ifdef NEED_STAT_VALS
+static int msb64(int64_t x);
+static MAP _stp_map_new_hstat_log(unsigned max_entries, int key_size, int buckets);
+static MAP _stp_map_new_hstat_linear(unsigned max_entries, int ksize, int start, int stop, int interval);
+static void _stp_map_print_histogram(MAP map, stat *s);
+#endif
void _stp_map_key_del(MAP map);
struct map_node * _stp_map_start(MAP map);
struct map_node * _stp_map_iter(MAP map, struct map_node *m);
void _stp_map_del(MAP map);
-void _stp_map_print_histogram(MAP map, stat *s);
void _stp_map_print(MAP map, const char *fmt);
static struct map_node * __stp_map_create(MAP map);
diff --git a/runtime/print.c b/runtime/print.c
index e7e97d27..3c09e105 100644
--- a/runtime/print.c
+++ b/runtime/print.c
@@ -2,12 +2,14 @@
#define _PRINT_C_
#include <linux/config.h>
-
+#include "string.h"
#include "io.c"
/** @file print.c
- * @addtogroup print Print Buffer
- * Print Buffer Functions.
+ * Printing Functions.
+ */
+
+/** @addtogroup print Print Functions
* The print buffer is for collecting output to send to the user daemon.
* This is a per-cpu static buffer. The buffer is sent when
* _stp_print_flush() is called.
@@ -127,6 +129,38 @@ void _stp_print_flush (void)
*/
#define _stp_print_string(str) _stp_string_cat_string(_stp_stdout,str)
+/* This function is used when printing maps or stats. */
+/* Probably belongs elsewhere, but is here for now. */
+/* It takes a format specification like those used for */
+/* printing maps and stats. It prints chars until it sees */
+/* a special format char (beginning with '%'. Then it */
+/* returns a pointer to that. */
+static char *next_fmt(char *fmt, int *num)
+{
+ char *f = fmt;
+ int in_fmt = 0;
+ dbug ("next_fmt %s\n", fmt);
+ *num = 0;
+ while (*f) {
+ if (in_fmt) {
+ if (*f == '%') {
+ _stp_string_cat_char(_stp_stdout,'%');
+ in_fmt = 0;
+ } else if (*f > '0' && *f <= '9') {
+ *num = *f - '0';
+ f++;
+ return f;
+ } else
+ return f;
+ } else if (*f == '%')
+ in_fmt = 1;
+ else
+ _stp_string_cat_char(_stp_stdout,*f);
+ f++;
+ }
+ return f;
+}
+
/** Write a String or C string into the print buffer.
* This macro selects the proper function to call.
* @param str A String or C string (char *)
diff --git a/runtime/stat-common.c b/runtime/stat-common.c
new file mode 100644
index 00000000..d136ec99
--- /dev/null
+++ b/runtime/stat-common.c
@@ -0,0 +1,229 @@
+#ifndef _STAT_COMMON_C_ /* -*- linux-c -*- */
+#define _STAT_COMMON_C_
+
+/* common stats functions for aggragations and maps */
+
+#include "stat.h"
+
+static int needed_space(int64_t v)
+{
+ int space = 0;
+
+ if (v == 0)
+ return 1;
+
+ if (v < 0) {
+ space++;
+ v = -v;
+ }
+ while (v) {
+ /* v /= 10; */
+ do_div (v, 10);
+ space++;
+ }
+ return space;
+}
+
+static void reprint (int num, char *s)
+{
+ while (num > 0) {
+ _stp_print_cstr (s);
+ num--;
+ }
+}
+
+/* implements a log base 2 function, or Most Significant Bit */
+/* with bits from 1 (lsb) to 64 (msb) */
+/* msb64(0) = 0 */
+/* msb64(1) = 1 */
+/* msb64(8) = 4 */
+/* msb64(512) = 10 */
+
+static int msb64(int64_t val)
+{
+ int res = 64;
+
+ if (val == 0)
+ return 0;
+
+ /* shortcut. most values will be 16-bit */
+ if (val & 0xffffffffffff0000ull) {
+ if (!(val & 0xffffffff00000000ull)) {
+ val <<= 32;
+ res -= 32;
+ }
+
+ if (!(val & 0xffff000000000000ull)) {
+ val <<= 16;
+ res -= 16;
+ }
+ } else {
+ val <<= 48;
+ res -= 48;
+ }
+
+ if (!(val & 0xff00000000000000ull)) {
+ val <<= 8;
+ res -= 8;
+ }
+
+ if (!(val & 0xf000000000000000ull)) {
+ val <<= 4;
+ res -= 4;
+ }
+
+ if (!(val & 0xc000000000000000ull)) {
+ val <<= 2;
+ res -= 2;
+ }
+
+ if (!(val & 0x8000000000000000ull)) {
+ val <<= 1;
+ res -= 1;
+ }
+
+ return res;
+}
+
+#ifndef HIST_WIDTH
+#define HIST_WIDTH 50
+#endif
+
+static void _stp_stat_print_histogram (Stat st, stat *sd)
+{
+ int scale, i, j, val_space, cnt_space;
+ int64_t val, v, max = 0;
+
+ if (st->hist_type != HIST_LOG && st->hist_type != HIST_LINEAR)
+ return;
+
+ /* get the maximum value, for scaling */
+ for (i = 0; i < st->hist_buckets; i++)
+ if (sd->histogram[i] > max)
+ max = sd->histogram[i];
+
+ if (max <= HIST_WIDTH)
+ scale = 1;
+ else {
+ int64_t tmp = max;
+ int rem = do_div (tmp, HIST_WIDTH);
+ scale = tmp;
+ if (rem) scale++;
+ }
+
+ cnt_space = needed_space (max);
+ if (st->hist_type == HIST_LINEAR)
+ val_space = needed_space (st->hist_start + st->hist_int * (st->hist_buckets - 1));
+ else
+ val_space = needed_space (1 << (st->hist_buckets - 1));
+ dbug ("max=%lld scale=%d val_space=%d\n", max, scale, val_space);
+
+ /* print header */
+ j = 0;
+ if (val_space > 5) /* 5 = sizeof("value") */
+ j = val_space - 5;
+ else
+ val_space = 5;
+ for ( i = 0; i < j; i++)
+ _stp_print_cstr (" ");
+ _stp_print_cstr("value |");
+ reprint (HIST_WIDTH, "-");
+ _stp_print_cstr (" count\n");
+ _stp_print_flush();
+ if (st->hist_type == HIST_LINEAR)
+ val = st->hist_start;
+ else
+ val = 0;
+ for (i = 0; i < st->hist_buckets; i++) {
+ reprint (val_space - needed_space(val), " ");
+ _stp_printf("%d", val);
+ _stp_print_cstr (" |");
+
+ /* v = s->histogram[i] / scale; */
+ v = sd->histogram[i];
+ do_div (v, scale);
+
+ reprint (v, "@");
+ reprint (HIST_WIDTH - v + 1 + cnt_space - needed_space(sd->histogram[i]), " ");
+ _stp_printf ("%lld\n", sd->histogram[i]);
+ if (st->hist_type == HIST_LINEAR)
+ val += st->hist_int;
+ else if (val == 0)
+ val = 1;
+ else
+ val *= 2;
+ }
+}
+
+static void _stp_stat_print_valtype (char *fmt, Stat st, struct stat_data *sd, int cpu)
+{
+ dbug ("fmt=%c\n", *fmt);
+ switch (*fmt) {
+ case 'C':
+ _stp_printf("%lld", sd->count);
+ break;
+ case 'm':
+ _stp_printf("%lld", sd->min);
+ break;
+ case 'M':
+ _stp_printf("%lld", sd->max);
+ break;
+ case 'S':
+ _stp_printf("%lld", sd->sum);
+ break;
+ case 'A':
+ {
+ int64_t avg = 0;
+ if (sd->count) {
+ avg = sd->sum;
+ do_div (avg, (int)sd->count); /* FIXME: check for overflow */
+ }
+ _stp_printf("%lld", avg);
+ break;
+ }
+ case 'H':
+ _stp_stat_print_histogram (st, sd);
+ _stp_print_flush();
+ break;
+ case 'c':
+ _stp_printf("%d", cpu);
+ break;
+ }
+}
+
+static void __stp_stat_add (Stat st, struct stat_data *sd, int64_t val)
+{
+ int n;
+ if (sd->count == 0) {
+ sd->count = 1;
+ sd->sum = sd->min = sd->max = val;
+ } else {
+ sd->count++;
+ sd->sum += val;
+ if (val > sd->max)
+ sd->max = val;
+ if (val < sd->min)
+ sd->min = val;
+ }
+ switch (st->hist_type) {
+ case HIST_LOG:
+ n = msb64 (val);
+ if (n >= st->hist_buckets)
+ n = st->hist_buckets - 1;
+ sd->histogram[n]++;
+ break;
+ case HIST_LINEAR:
+ val -= st->hist_start;
+ do_div (val, st->hist_int);
+ n = val;
+ if (n < 0)
+ n = 0;
+ if (n >= st->hist_buckets)
+ n = st->hist_buckets - 1;
+ sd->histogram[n]++;
+ default:
+ break;
+ }
+}
+
+#endif /* _STAT_COMMON_C_ */
diff --git a/runtime/stat.c b/runtime/stat.c
new file mode 100644
index 00000000..f57dea65
--- /dev/null
+++ b/runtime/stat.c
@@ -0,0 +1,254 @@
+#ifndef _STAT_C_ /* -*- linux-c -*- */
+#define _STAT_C_
+
+/** @file stat.c
+ * @brief Statistics Aggregation
+ */
+/** @addtogroup stat Statistics Aggregation
+ * The Statistics aggregations keep per-cpu statistics. You
+ * must create all aggregations at probe initialization and it is
+ * best to not read them until probe exit. If you must read them
+ * while probes are running, the values may be slightly off due
+ * to a probe updating the statistics of one cpu while another cpu attempts
+ * to read the same data. This will also negatively impact performance.
+ *
+ * If you have a need to poll Stat data while probes are running, and
+ * you want to be sure the data is accurate, you can do
+ * @verbatim
+#define NEED_STAT_LOCKS 1
+@endverbatim
+ * This will insert per-cpu spinlocks around all accesses to Stat data,
+ * which will reduce performance some.
+ *
+ * Stats keep track of count, sum, min and max. Average is computed
+ * from the sum and count when required. Histograms are optional.
+ * If you want a histogram, you must set "type" to HIST_LOG
+ * or HIST_LINEAR when you call _stp_stat_init().
+ *
+ * @{
+ */
+
+#include "stat-common.c"
+
+/* for the paranoid. */
+#if NEED_STAT_LOCKS == 1
+#define STAT_LOCK(st) spin_lock(&st->lock)
+#define STAT_UNLOCK(st) spin_unlock(&st->lock)
+#else
+#define STAT_LOCK(st) ;
+#define STAT_UNLOCK(st) ;
+#endif
+
+/** Initialize a Stat.
+ * Call this during probe initialization to create a Stat.
+ *
+ * @param type HIST_NONE, HIST_LOG, or HIST_LINEAR
+ *
+ * For HIST_LOG, the following additional parametrs are required:
+ * @param buckets - An integer specifying the number of buckets.
+ *
+ * For HIST_LINEAR, the following additional parametrs are required:
+ * @param start - An integer. The start of the histogram.
+ * @param stop - An integer. The stopping value. Should be > start.
+ * @param interval - An integer. The interval.
+ */
+Stat _stp_stat_init (int type, ...)
+{
+ int size, buckets=0, start=0, stop=0, interval=0;
+ struct stat_data *sd, *agg;
+ Stat st;
+
+ if (type != HIST_NONE) {
+ va_list ap;
+ va_start (ap, type);
+
+ if (type == HIST_LOG) {
+ buckets = va_arg(ap, int);
+ } else {
+ start = va_arg(ap, int);
+ stop = va_arg(ap, int);
+ interval = va_arg(ap, int);
+ /* FIXME. check interval != 0 and not too large */
+ buckets = (stop - start) / interval;
+ if ((stop - start) % interval) buckets++;
+ }
+ va_end (ap);
+ }
+ st = (Stat) kmalloc (sizeof(struct _Stat), GFP_KERNEL);
+ if (st == NULL)
+ return NULL;
+
+ size = buckets * sizeof(int64_t) + sizeof(struct stat_data);
+ sd = (struct stat_data *) __alloc_percpu (size, 8);
+ if (sd == NULL)
+ goto exit1;
+
+#if NEED_STAT_LOCKS == 1
+ {
+ int i;
+ for_each_cpu(i) {
+ struct stat_data *sdp = per_cpu_ptr (sd, i);
+ sdp->lock = SPIN_LOCK_UNLOCKED;
+ }
+ }
+#endif
+
+ agg = (struct stat_data *)kmalloc (size, GFP_KERNEL);
+ if (agg == NULL)
+ goto exit2;
+
+ st->hist_type = type;
+ st->hist_start = start;
+ st->hist_stop = stop;
+ st->hist_int = interval;
+ st->hist_buckets = buckets;
+ st->sd = sd;
+ st->agg = agg;
+ return st;
+
+exit2:
+ kfree (sd);
+exit1:
+ kfree (st);
+ return NULL;
+}
+
+/** Add to a Stat.
+ * Add an int64 to a Stat.
+ *
+ * @param st Stat
+ * @param val Value to add
+ */
+void _stp_stat_add (Stat st, int64_t val)
+{
+ struct stat_data *sd = per_cpu_ptr (st->sd, get_cpu());
+ STAT_LOCK(sd);
+ __stp_stat_add (st, sd, val);
+ STAT_UNLOCK(sd);
+ put_cpu();
+}
+
+/** Get per-cpu Stats.
+ * Gets the Stats for a specific CPU.
+ *
+ * If NEED_STAT_LOCKS is set, you MUST call STAT_UNLOCK()
+ * when you are finished with the returned pointer.
+ *
+ * @param st Stat
+ * @param cpu CPU number
+ * @returns A pointer to a struct stat_data.
+ */
+struct stat_data *_stp_stat_get_cpu (Stat st, int cpu)
+{
+ struct stat_data *sd = per_cpu_ptr (st->sd, cpu);
+ STAT_LOCK(sd);
+ return sd;
+}
+
+static void _stp_stat_clear_data (Stat st, struct stat_data *sd)
+{
+ int j;
+ sd->count = sd->sum = sd->min = sd->max = 0;
+ if (st->hist_type != HIST_NONE) {
+ for (j = 0; j < st->hist_buckets; j++)
+ sd->histogram[j] = 0;
+ }
+}
+
+/** Get Stats.
+ * Gets the aggregated Stats for all CPUs.
+ *
+ * If NEED_STAT_LOCKS is set, you MUST call STAT_UNLOCK()
+ * when you are finished with the returned pointer.
+ *
+ * @param st Stat
+ * @param clear Set if you want the data cleared after the read. Useful
+ * for polling.
+ * @returns A pointer to a struct stat_data.
+ */
+struct stat_data *_stp_stat_get (Stat st, int clear)
+{
+ int i, j;
+ struct stat_data *agg = st->agg;
+ STAT_LOCK(agg);
+ _stp_stat_clear_data (st, agg);
+
+ for_each_cpu(i) {
+ struct stat_data *sd = per_cpu_ptr (st->sd, i);
+ STAT_LOCK(sd);
+ if (sd->count) {
+ if (agg->count == 0) {
+ agg->min = sd->min;
+ agg->max = sd->max;
+ }
+ agg->count += sd->count;
+ agg->sum += sd->sum;
+ if (sd->max > agg->max)
+ agg->max = sd->max;
+ if (sd->min < agg->min)
+ agg->min = sd->min;
+ if (st->hist_type != HIST_NONE) {
+ for (j = 0; j < st->hist_buckets; j++)
+ agg->histogram[j] += sd->histogram[j];
+ }
+ if (clear)
+ _stp_stat_clear_data (st, sd);
+ }
+ STAT_UNLOCK(sd);
+ }
+ return agg;
+}
+
+
+static void __stp_stat_print (char *fmt, Stat st, struct stat_data *sd, int cpu)
+{
+ int num;
+ char *f = (char *)fmt;
+ while (*f) {
+ f = next_fmt (f, &num);
+ _stp_stat_print_valtype (f, st, sd, cpu);
+ if (*f)
+ f++;
+ }
+ _stp_print_cstr ("\n");
+ _stp_print_flush();
+}
+
+/** Print per-cpu Stats.
+ * Prints the Stats for each CPU.
+ *
+ * @param st Stat
+ * @param fmt @ref format_string
+ * @param clear Set if you want the data cleared after the read. Useful
+ * for polling.
+ */
+void _stp_stat_print_cpu (Stat st, char *fmt, int clear)
+{
+ int i;
+ for_each_cpu(i) {
+ struct stat_data *sd = per_cpu_ptr (st->sd, i);
+ STAT_LOCK(sd);
+ __stp_stat_print (fmt, st, sd, i);
+ if (clear)
+ _stp_stat_clear_data (st, sd);
+ STAT_UNLOCK(sd);
+ }
+}
+
+/** Print Stats.
+ * Prints the Stats.
+ *
+ * @param st Stat
+ * @param fmt @ref format_string
+ * @param clear Set if you want the data cleared after the read. Useful
+ * for polling.
+ */
+void _stp_stat_print (Stat st, char *fmt, int clear)
+{
+ stat *agg = _stp_stat_get(st, clear);
+ __stp_stat_print (fmt, st, agg, 0);
+ STAT_UNLOCK(agg);
+}
+/** @} */
+#endif /* _STAT_C_ */
+
diff --git a/runtime/stat.h b/runtime/stat.h
new file mode 100644
index 00000000..50dfc458
--- /dev/null
+++ b/runtime/stat.h
@@ -0,0 +1,37 @@
+#ifndef _STAT_H_ /* -*- linux-c -*- */
+#define _STAT_H_
+
+#ifndef NEED_STAT_LOCKS
+#define NEED_STAT_LOCKS 0
+#endif
+
+/** histogram type */
+enum histtype { HIST_NONE, HIST_LOG, HIST_LINEAR };
+
+/** Statistics are stored in this struct. This is per-cpu or per-node data
+ and is variable length due to the unknown size of the histogram. */
+struct stat_data {
+ int64_t count;
+ int64_t sum;
+ int64_t min, max;
+#if NEED_STAT_LOCKS == 1
+ spinlock_t lock;
+#endif
+ int64_t histogram[];
+};
+
+typedef struct stat_data stat;
+
+struct _Stat {
+ enum histtype hist_type;
+ int hist_start;
+ int hist_stop;
+ int hist_int;
+ int hist_buckets;
+ struct stat_data *sd;
+ struct stat_data *agg;
+};
+
+typedef struct _Stat *Stat;
+
+#endif /* _STAT_H_ */
diff --git a/runtime/string.c b/runtime/string.c
index 4d5a9c83..6ef26190 100644
--- a/runtime/string.c
+++ b/runtime/string.c
@@ -1,44 +1,17 @@
#ifndef _STRING_C_ /* -*- linux-c -*- */
#define _STRING_C_
-#ifndef STP_NUM_STRINGS
-#define STP_NUM_STRINGS 0
-#endif
-
#include <linux/config.h>
+#include "string.h"
/** @file string.c
* @brief Implements String type.
*/
/** @addtogroup string String Functions
*
- * One of the biggest restrictions the library has is that it cannot allocate things like strings off the stack.
- * It is also not a good idea to dynamically allocate space for strings with kmalloc(). That leaves us with
- * statically allocated space for strings. This is what is implemented in the String module. Strings use
- * preallocated per-cpu buffers and are safe to use (unlike C strings).
* @{
*/
-/** Maximum string size allowed in Strings */
-#ifndef STP_STRING_SIZE
-#define STP_STRING_SIZE 2048
-#endif
-
-struct string {
- int len;
- char buf[STP_STRING_SIZE];
-};
-
-static struct string _stp_string[STP_NUM_STRINGS][NR_CPUS];
-
-typedef struct string *String;
-
-/* set up a special stdout string */
-static struct string __stp_stdout;
-String _stp_stdout = &__stp_stdout;
-
-void _stp_vsprintf (String str, const char *fmt, va_list args);
-
/** Initialize a String for our use.
* This grabs one of the global Strings for our temporary use.
*
@@ -212,7 +185,6 @@ char * _stp_string_ptr (String str)
return str->buf;
}
-
/** ConCATenate (append) a String or C string to a String.
* This macro selects the proper function to call.
* @param str1 A String
diff --git a/runtime/string.h b/runtime/string.h
new file mode 100644
index 00000000..e7168e00
--- /dev/null
+++ b/runtime/string.h
@@ -0,0 +1,30 @@
+#ifndef _STRING_H_ /* -*- linux-c -*- */
+#define _STRING_H_
+
+/** Maximum string size allowed in Strings */
+#ifndef STP_STRING_SIZE
+#define STP_STRING_SIZE 2048
+#endif
+
+/** Maximum number of strings a probe uses. */
+#ifndef STP_NUM_STRINGS
+#define STP_NUM_STRINGS 0
+#endif
+
+struct string {
+ int len;
+ char buf[STP_STRING_SIZE];
+};
+
+static struct string _stp_string[STP_NUM_STRINGS][NR_CPUS];
+
+typedef struct string *String;
+
+/* set up a special stdout string */
+static struct string __stp_stdout = {0};
+String _stp_stdout = &__stp_stdout;
+
+void _stp_vsprintf (String str, const char *fmt, va_list args);
+void _stp_string_cat_char (String str1, const char c);
+
+#endif /* _STRING_H_ */