diff options
author | hunt <hunt> | 2005-06-18 07:24:42 +0000 |
---|---|---|
committer | hunt <hunt> | 2005-06-18 07:24:42 +0000 |
commit | e5d2abb56ca57a613e4ef9398a6499190b2265be (patch) | |
tree | 083465047d73bc498f2278d2eb3dc8514b455ad3 | |
parent | ef0e92b0335077884edd5ac9997a203bec1839b0 (diff) | |
download | systemtap-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/ChangeLog | 24 | ||||
-rw-r--r-- | runtime/Doxyfile | 14 | ||||
-rw-r--r-- | runtime/README | 143 | ||||
-rw-r--r-- | runtime/README.doc | 176 | ||||
-rw-r--r-- | runtime/counter.c | 136 | ||||
-rw-r--r-- | runtime/docs/examples/argv.c | 5 | ||||
-rw-r--r-- | runtime/docs/examples/foreach.c | 10 | ||||
-rw-r--r-- | runtime/docs/examples/list.c | 7 | ||||
-rw-r--r-- | runtime/docs/examples/map.c | 2 | ||||
-rw-r--r-- | runtime/io.c | 4 | ||||
-rw-r--r-- | runtime/list.c | 3 | ||||
-rw-r--r-- | runtime/map-int.c | 73 | ||||
-rw-r--r-- | runtime/map-keys.c | 9 | ||||
-rw-r--r-- | runtime/map-stat.c | 84 | ||||
-rw-r--r-- | runtime/map-str.c | 118 | ||||
-rw-r--r-- | runtime/map-values.c | 156 | ||||
-rw-r--r-- | runtime/map.c | 429 | ||||
-rw-r--r-- | runtime/map.doc | 129 | ||||
-rw-r--r-- | runtime/map.h | 37 | ||||
-rw-r--r-- | runtime/print.c | 40 | ||||
-rw-r--r-- | runtime/stat-common.c | 229 | ||||
-rw-r--r-- | runtime/stat.c | 254 | ||||
-rw-r--r-- | runtime/stat.h | 37 | ||||
-rw-r--r-- | runtime/string.c | 30 | ||||
-rw-r--r-- | runtime/string.h | 30 |
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_ */ |