summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhunt <hunt>2005-04-07 15:17:29 +0000
committerhunt <hunt>2005-04-07 15:17:29 +0000
commit3d4bc8bea6b45893bd4b49f44df26bd602b4cba5 (patch)
treebae3fb1bc5fcbd7906574b6902c649b203274a3f
parent6e01db401be4a19e6d81d9955d055e2f4e1b6aea (diff)
downloadsystemtap-steved-3d4bc8bea6b45893bd4b49f44df26bd602b4cba5.tar.gz
systemtap-steved-3d4bc8bea6b45893bd4b49f44df26bd602b4cba5.tar.xz
systemtap-steved-3d4bc8bea6b45893bd4b49f44df26bd602b4cba5.zip
Update to use relayfs.
-rw-r--r--runtime/ChangeLog9
-rw-r--r--runtime/copy.c17
-rw-r--r--runtime/docs/html/dir_000003.html2
-rw-r--r--runtime/docs/html/dir_000004.html2
-rw-r--r--runtime/docs/html/dtr_8mod_8c-source.html63
-rw-r--r--runtime/docs/html/files.html2
-rw-r--r--runtime/docs/html/globals.html3
-rw-r--r--runtime/docs/html/globals_func.html3
-rw-r--r--runtime/docs/html/group__scbuf.html70
-rw-r--r--runtime/docs/html/group__stack.html51
-rw-r--r--runtime/docs/html/group__sym.html2
-rw-r--r--runtime/docs/html/kprobe__where__funct_8c-source.html4
-rw-r--r--runtime/docs/html/scbuf_8c-source.html164
-rw-r--r--runtime/docs/html/stack_8c-source.html43
-rw-r--r--runtime/docs/html/stack_8c.html4
-rw-r--r--runtime/docs/html/structmap__root.html2
-rw-r--r--runtime/docs/html/test4_2dtr_8c-source.html371
-rw-r--r--runtime/docs/html/todo.html7
-rw-r--r--runtime/io.c95
-rw-r--r--runtime/map.c14
-rw-r--r--runtime/map.h20
-rw-r--r--runtime/print.c168
-rw-r--r--runtime/probes.c16
-rw-r--r--runtime/relay-app.h534
-rw-r--r--runtime/relayfs/Makefile12
-rw-r--r--runtime/relayfs/buffers.c195
-rw-r--r--runtime/relayfs/buffers.h12
-rwxr-xr-xruntime/relayfs/build4
-rw-r--r--runtime/relayfs/inode.c423
-rw-r--r--runtime/relayfs/linux/relayfs_fs.h263
-rw-r--r--runtime/relayfs/relay.c530
-rw-r--r--runtime/relayfs/relay.h12
-rw-r--r--runtime/relayfs/relayfs.txt206
-rw-r--r--runtime/runtime.h2
-rw-r--r--runtime/scbuf.c71
-rw-r--r--runtime/stack.c54
-rw-r--r--runtime/stpd/Makefile7
-rw-r--r--runtime/stpd/librelay.c520
-rw-r--r--runtime/stpd/librelay.h51
-rw-r--r--runtime/stpd/stpd.c98
-rw-r--r--runtime/string.c130
-rw-r--r--runtime/sym.c48
42 files changed, 3861 insertions, 443 deletions
diff --git a/runtime/ChangeLog b/runtime/ChangeLog
index 97981580..cb062f99 100644
--- a/runtime/ChangeLog
+++ b/runtime/ChangeLog
@@ -1,9 +1,6 @@
-2005-03-21 Martin Hunt <hunt@redhat.com>
+2005-03-30 Martin Hunt <hunt@redhat.com>
- * io.c (_stp_print_buf): Add print buffer stuff and other misc.
- Will move later.
+ * scbuf.c: Make functions use per-cpu buffers as documented.
+ _stp_scbuf_clear(): Now returns a pointer to the buffer.
- * map.c (_stp_map_add_int64): New function.
-
- * probes: Directory of example probes.
diff --git a/runtime/copy.c b/runtime/copy.c
index f4b35106..6e2313f6 100644
--- a/runtime/copy.c
+++ b/runtime/copy.c
@@ -1,7 +1,8 @@
-#ifndef _COPY_C_
+#ifndef _COPY_C_ /* -*- linux-c -*- */
#define _COPY_C_
-/* -*- linux-c -*- */
+#include "string.c"
+
/** @file copy.c
* @brief Functions to copy from user space.
*/
@@ -98,6 +99,18 @@ _stp_strncpy_from_user(char *dst, const char __user *src, long count)
return res;
}
+void _stp_string_from_user (String str, const char __user *src, long count)
+{
+ long res;
+ if (count > STP_STRING_SIZE - str->len - 1)
+ count = STP_STRING_SIZE - str->len - 1;
+ __stp_strncpy_from_user(str->buf + str->len, src, count, res);
+ if (res > 0) {
+ str->len += res;
+ str->buf[str->len] = '\0';
+ }
+}
+
/** Copy a block of data from user space.
*
* If some data could not be copied, this function will pad the copied
diff --git a/runtime/docs/html/dir_000003.html b/runtime/docs/html/dir_000003.html
index 173623b3..b9e20e19 100644
--- a/runtime/docs/html/dir_000003.html
+++ b/runtime/docs/html/dir_000003.html
@@ -13,6 +13,8 @@
<tr><td colspan="2"><br><h2>Files</h2></td></tr>
<tr><td class="memItemLeft" nowrap align="right" valign="top">file &nbsp;</td><td class="memItemRight" valign="bottom"><b>dtr.c</b> <a href="test4_2dtr_8c-source.html">[code]</a></td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">file &nbsp;</td><td class="memItemRight" valign="bottom"><b>dtr.mod.c</b> <a href="dtr_8mod_8c-source.html">[code]</a></td></tr>
+
<tr><td class="memItemLeft" nowrap align="right" valign="top">file &nbsp;</td><td class="memItemRight" valign="bottom"><b>nrec.c</b> <a href="nrec_8c-source.html">[code]</a></td></tr>
<tr><td class="memItemLeft" nowrap align="right" valign="top">file &nbsp;</td><td class="memItemRight" valign="bottom"><b>README</b> <a href="probes_2test4_2README-source.html">[code]</a></td></tr>
diff --git a/runtime/docs/html/dir_000004.html b/runtime/docs/html/dir_000004.html
index f2294de2..d3e72861 100644
--- a/runtime/docs/html/dir_000004.html
+++ b/runtime/docs/html/dir_000004.html
@@ -13,6 +13,8 @@
<tr><td colspan="2"><br><h2>Files</h2></td></tr>
<tr><td class="memItemLeft" nowrap align="right" valign="top">file &nbsp;</td><td class="memItemRight" valign="bottom"><b>kprobe_where_funct.c</b> <a href="kprobe__where__funct_8c-source.html">[code]</a></td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">file &nbsp;</td><td class="memItemRight" valign="bottom"><b>kprobe_where_funct.mod.c</b> <a href="kprobe__where__funct_8mod_8c-source.html">[code]</a></td></tr>
+
<tr><td class="memItemLeft" nowrap align="right" valign="top">file &nbsp;</td><td class="memItemRight" valign="bottom"><b>README</b> <a href="probes_2where__func_2README-source.html">[code]</a></td></tr>
</table>
diff --git a/runtime/docs/html/dtr_8mod_8c-source.html b/runtime/docs/html/dtr_8mod_8c-source.html
index 1deec9c6..e497c5c4 100644
--- a/runtime/docs/html/dtr_8mod_8c-source.html
+++ b/runtime/docs/html/dtr_8mod_8c-source.html
@@ -3,7 +3,7 @@
<title>SystemTap: probes/test4/dtr.mod.c Source File</title>
<link href="doxygen.css" rel="stylesheet" type="text/css">
</head><body>
-<!-- Generated by Doxygen 1.3.9.1 -->
+<!-- Generated by Doxygen 1.4.1 -->
<div class="qindex"><a class="qindex" href="index.html">Main&nbsp;Page</a> | <a class="qindex" href="modules.html">Modules</a> | <a class="qindex" href="annotated.html">Data&nbsp;Structures</a> | <a class="qindex" href="dirs.html">Directories</a> | <a class="qindex" href="files.html">File&nbsp;List</a> | <a class="qindex" href="functions.html">Data&nbsp;Fields</a> | <a class="qindex" href="globals.html">Globals</a> | <a class="qindex" href="pages.html">Related&nbsp;Pages</a></div>
<div class="nav">
<a class="el" href="dir_000000.html">probes</a>&nbsp;/&nbsp;<a class="el" href="dir_000003.html">test4</a></div>
@@ -27,30 +27,39 @@
00018 __attribute_used__
00019 __attribute__((section("__versions"))) = {
00020 { 0x506abef7, <span class="stringliteral">"struct_module"</span> },
-00021 { 0xb240ca65, <span class="stringliteral">"schedule_work"</span> },
-00022 { 0x1b9aca3f, <span class="stringliteral">"jprobe_return"</span> },
-00023 { 0xe3b0192b, <span class="stringliteral">"vscnprintf"</span> },
-00024 { 0x45e5f47f, <span class="stringliteral">"register_kprobe"</span> },
-00025 { 0x1e736243, <span class="stringliteral">"unregister_kprobe"</span> },
-00026 { 0xdd03a51e, <span class="stringliteral">"register_jprobe"</span> },
-00027 { 0x49a83d3a, <span class="stringliteral">"unregister_jprobe"</span> },
-00028 { 0x3fa03a97, <span class="stringliteral">"memset"</span> },
-00029 { 0xc16fe12d, <span class="stringliteral">"__memcpy"</span> },
-00030 { 0xe914e41e, <span class="stringliteral">"strcpy"</span> },
-00031 { 0xe2d5255a, <span class="stringliteral">"strcmp"</span> },
-00032 { 0x2fd1d81c, <span class="stringliteral">"vfree"</span> },
-00033 { 0x37a0cba, <span class="stringliteral">"kfree"</span> },
-00034 { 0xd6ee688f, <span class="stringliteral">"vmalloc"</span> },
-00035 { 0x8ce16b3f, <span class="stringliteral">"__kmalloc"</span> },
-00036 { 0x8e3c9cc3, <span class="stringliteral">"vprintk"</span> },
-00037 { 0xdd132261, <span class="stringliteral">"printk"</span> },
-00038 };
-00039
-00040 <span class="keyword">static</span> <span class="keyword">const</span> <span class="keywordtype">char</span> __module_depends[]
-00041 __attribute_used__
-00042 __attribute__((section(<span class="stringliteral">".modinfo"</span>))) =
-00043 <span class="stringliteral">"depends="</span>;
-00044
-00045
-00046 MODULE_INFO(srcversion, <span class="stringliteral">"3CC4F9300C5C4E353BD2804"</span>);
+00021 { 0xfcdec747, <span class="stringliteral">"sock_release"</span> },
+00022 { 0x8968634b, <span class="stringliteral">"netlink_kernel_create"</span> },
+00023 { 0xb240ca65, <span class="stringliteral">"schedule_work"</span> },
+00024 { 0x1b9aca3f, <span class="stringliteral">"jprobe_return"</span> },
+00025 { 0x757b6858, <span class="stringliteral">"skb_dequeue"</span> },
+00026 { 0xd4aed67a, <span class="stringliteral">"netlink_ack"</span> },
+00027 { 0x5fc0695e, <span class="stringliteral">"__kfree_skb"</span> },
+00028 { 0xaa1449d7, <span class="stringliteral">"netlink_unicast"</span> },
+00029 { 0x3958414f, <span class="stringliteral">"skb_over_panic"</span> },
+00030 { 0x559dbbb8, <span class="stringliteral">"alloc_skb"</span> },
+00031 { 0x7ec9bfbc, <span class="stringliteral">"strncpy"</span> },
+00032 { 0xe3b0192b, <span class="stringliteral">"vscnprintf"</span> },
+00033 { 0x45e5f47f, <span class="stringliteral">"register_kprobe"</span> },
+00034 { 0x1e736243, <span class="stringliteral">"unregister_kprobe"</span> },
+00035 { 0xdd03a51e, <span class="stringliteral">"register_jprobe"</span> },
+00036 { 0x49a83d3a, <span class="stringliteral">"unregister_jprobe"</span> },
+00037 { 0x3fa03a97, <span class="stringliteral">"memset"</span> },
+00038 { 0xc16fe12d, <span class="stringliteral">"__memcpy"</span> },
+00039 { 0xe914e41e, <span class="stringliteral">"strcpy"</span> },
+00040 { 0xe2d5255a, <span class="stringliteral">"strcmp"</span> },
+00041 { 0x2fd1d81c, <span class="stringliteral">"vfree"</span> },
+00042 { 0x37a0cba, <span class="stringliteral">"kfree"</span> },
+00043 { 0xd6ee688f, <span class="stringliteral">"vmalloc"</span> },
+00044 { 0x8ce16b3f, <span class="stringliteral">"__kmalloc"</span> },
+00045 { 0x8e3c9cc3, <span class="stringliteral">"vprintk"</span> },
+00046 { 0xdd132261, <span class="stringliteral">"printk"</span> },
+00047 };
+00048
+00049 <span class="keyword">static</span> <span class="keyword">const</span> <span class="keywordtype">char</span> __module_depends[]
+00050 __attribute_used__
+00051 __attribute__((section(<span class="stringliteral">".modinfo"</span>))) =
+00052 <span class="stringliteral">"depends="</span>;
+00053
+00054
+00055 MODULE_INFO(srcversion, <span class="stringliteral">"01D3B50188E5E952DA9FD12"</span>);
</pre></div></body></html>
diff --git a/runtime/docs/html/files.html b/runtime/docs/html/files.html
index ef853864..f61591f6 100644
--- a/runtime/docs/html/files.html
+++ b/runtime/docs/html/files.html
@@ -24,9 +24,11 @@
<tr><td class="indexkey">probes/tasklet/<b>README</b> <a href="probes_2tasklet_2README-source.html">[code]</a></td><td class="indexvalue"></td></tr>
<tr><td class="indexkey">probes/tasklet/<b>stp_tasklet.c</b> <a href="stp__tasklet_8c-source.html">[code]</a></td><td class="indexvalue"></td></tr>
<tr><td class="indexkey">probes/test4/<b>dtr.c</b> <a href="test4_2dtr_8c-source.html">[code]</a></td><td class="indexvalue"></td></tr>
+ <tr><td class="indexkey">probes/test4/<b>dtr.mod.c</b> <a href="dtr_8mod_8c-source.html">[code]</a></td><td class="indexvalue"></td></tr>
<tr><td class="indexkey">probes/test4/<b>nrec.c</b> <a href="nrec_8c-source.html">[code]</a></td><td class="indexvalue"></td></tr>
<tr><td class="indexkey">probes/test4/<b>README</b> <a href="probes_2test4_2README-source.html">[code]</a></td><td class="indexvalue"></td></tr>
<tr><td class="indexkey">probes/where_func/<b>kprobe_where_funct.c</b> <a href="kprobe__where__funct_8c-source.html">[code]</a></td><td class="indexvalue"></td></tr>
+ <tr><td class="indexkey">probes/where_func/<b>kprobe_where_funct.mod.c</b> <a href="kprobe__where__funct_8mod_8c-source.html">[code]</a></td><td class="indexvalue"></td></tr>
<tr><td class="indexkey">probes/where_func/<b>README</b> <a href="probes_2where__func_2README-source.html">[code]</a></td><td class="indexvalue"></td></tr>
</table>
</body></html>
diff --git a/runtime/docs/html/globals.html b/runtime/docs/html/globals.html
index 74f0f2e2..7ac847b4 100644
--- a/runtime/docs/html/globals.html
+++ b/runtime/docs/html/globals.html
@@ -50,7 +50,8 @@ Here is a list of all documented functions, variables, defines, enums, and typed
: <a class="el" href="group__io.html#ga2">io.c</a><li>_stp_register_jprobes()
: <a class="el" href="probes_8c.html#a2">probes.c</a><li>_stp_register_kprobes()
: <a class="el" href="probes_8c.html#a4">probes.c</a><li>_stp_ret_addr()
-: <a class="el" href="group__current.html#ga0">current.c</a><li>_stp_strncpy_from_user()
+: <a class="el" href="group__current.html#ga0">current.c</a><li>_stp_stack_print()
+: <a class="el" href="group__stack.html#ga6">stack.c</a><li>_stp_strncpy_from_user()
: <a class="el" href="group__copy.html#ga0">copy.c</a><li>_stp_unregister_jprobes()
: <a class="el" href="probes_8c.html#a1">probes.c</a><li>_stp_unregister_kprobes()
: <a class="el" href="probes_8c.html#a3">probes.c</a><li>_stp_valloc()
diff --git a/runtime/docs/html/globals_func.html b/runtime/docs/html/globals_func.html
index 051cf88c..1562f9d2 100644
--- a/runtime/docs/html/globals_func.html
+++ b/runtime/docs/html/globals_func.html
@@ -46,7 +46,8 @@
: <a class="el" href="group__io.html#ga2">io.c</a><li>_stp_register_jprobes()
: <a class="el" href="probes_8c.html#a2">probes.c</a><li>_stp_register_kprobes()
: <a class="el" href="probes_8c.html#a4">probes.c</a><li>_stp_ret_addr()
-: <a class="el" href="group__current.html#ga0">current.c</a><li>_stp_strncpy_from_user()
+: <a class="el" href="group__current.html#ga0">current.c</a><li>_stp_stack_print()
+: <a class="el" href="group__stack.html#ga6">stack.c</a><li>_stp_strncpy_from_user()
: <a class="el" href="group__copy.html#ga0">copy.c</a><li>_stp_unregister_jprobes()
: <a class="el" href="probes_8c.html#a1">probes.c</a><li>_stp_unregister_kprobes()
: <a class="el" href="probes_8c.html#a3">probes.c</a><li>_stp_valloc()
diff --git a/runtime/docs/html/group__scbuf.html b/runtime/docs/html/group__scbuf.html
index c5e0d6e0..89033737 100644
--- a/runtime/docs/html/group__scbuf.html
+++ b/runtime/docs/html/group__scbuf.html
@@ -17,29 +17,26 @@
<tr><td class="memItemLeft" nowrap align="right" valign="top">void&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__scbuf.html#ga2">_stp_sprint</a> (const char *fmt,...)</td></tr>
<tr><td class="mdescLeft">&nbsp;</td><td class="mdescRight">Sprint into the scratch buffer. <a href="#ga2"></a><br></td></tr>
-<tr><td class="memItemLeft" nowrap align="right" valign="top"><a class="anchor" name="ga3" doxytag="scbuf::_stp_sprint_str"></a>
-void&nbsp;</td><td class="memItemRight" valign="bottom"><b>_stp_sprint_str</b> (const char *str)</td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">void&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__scbuf.html#ga3">_stp_sprint_str</a> (const char *str)</td></tr>
-<tr><td class="memItemLeft" nowrap align="right" valign="top">void&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__scbuf.html#ga4">_stp_scbuf_clear</a> (void)</td></tr>
+<tr><td class="mdescLeft">&nbsp;</td><td class="mdescRight">Write a string into the scratch buffer. <a href="#ga3"></a><br></td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">char *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__scbuf.html#ga4">_stp_scbuf_clear</a> (void)</td></tr>
<tr><td class="mdescLeft">&nbsp;</td><td class="mdescRight">Clear the scratch buffer. <a href="#ga4"></a><br></td></tr>
-<tr><td colspan="2"><br><h2>Variables</h2></td></tr>
-<tr><td class="memItemLeft" nowrap align="right" valign="top"><a class="anchor" name="ga0" doxytag="scbuf::_stp_scbuf"></a>
-char&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__scbuf.html#ga0">_stp_scbuf</a> [8191+1]</td></tr>
-
-<tr><td class="mdescLeft">&nbsp;</td><td class="mdescRight">Scratch buffer for printing, building strings, etc. <br></td></tr>
</table>
<hr><a name="_details"></a><h2>Detailed Description</h2>
Scratch Buffer Functions.
<p>
-The scratch buffer is for collecting output before storing in a map, printing, etc. This is a per-cpu static buffer. It is necessary because of the limited stack space available in the kernel. <hr><h2>Function Documentation</h2>
+The scratch buffer is for collecting output before storing in a map, printing, etc. This is a per-cpu static buffer. It is necessary because of the limited stack space available in the kernel. <p>
+<dl compact><dt><b><a class="el" href="todo.html#_todo000008">Todo:</a></b></dt><dd>Need careful review of these to insure safety.</dd></dl>
+<hr><h2>Function Documentation</h2>
<a class="anchor" name="ga4" doxytag="scbuf.c::_stp_scbuf_clear"></a><p>
<table class="mdTable" cellpadding="2" cellspacing="0">
<tr>
<td class="mdRow">
<table cellpadding="0" cellspacing="0" border="0">
<tr>
- <td class="md" nowrap valign="top">void _stp_scbuf_clear </td>
+ <td class="md" nowrap valign="top">char* _stp_scbuf_clear </td>
<td class="md" valign="top">(&nbsp;</td>
<td class="md" nowrap valign="top">void&nbsp;</td>
<td class="mdname1" valign="top" nowrap> </td>
@@ -60,11 +57,12 @@ The scratch buffer is for collecting output before storing in a map, printing, e
<p>
Clear the scratch buffer.
<p>
-Output from <a class="el" href="group__scbuf.html#ga2">_stp_sprint()</a> will accumulate in the buffer until this is called.
+This function should be called before anything is written to the scratch buffer. Output will accumulate in the buffer until this function is called again. <dl compact><dt><b>Returns:</b></dt><dd>A pointer to the buffer. </dd></dl>
+
<p>
-Definition at line <a class="el" href="scbuf_8c-source.html#l00059">59</a> of file <a class="el" href="scbuf_8c-source.html">scbuf.c</a>.
+Definition at line <a class="el" href="scbuf_8c-source.html#l00074">74</a> of file <a class="el" href="scbuf_8c-source.html">scbuf.c</a>.
<p>
-References <a class="el" href="scbuf_8c-source.html#l00018">_stp_scbuf</a>, and <a class="el" href="scbuf_8c-source.html#l00015">STP_BUF_LEN</a>. </td>
+References <a class="el" href="scbuf_8c-source.html#l00017">STP_BUF_LEN</a>. </td>
</tr>
</table>
<a class="anchor" name="ga2" doxytag="scbuf.c::_stp_sprint"></a><p>
@@ -103,7 +101,7 @@ References <a class="el" href="scbuf_8c-source.html#l00018">_stp_scbuf</a>, and
<p>
Sprint into the scratch buffer.
<p>
-Like printf, except output goes into <a class="el" href="group__scbuf.html#ga0">_stp_scbuf</a>, which will contain the null-terminated output. Safe because overflowing <a class="el" href="group__scbuf.html#ga0">_stp_scbuf</a> is not allowed. Size is limited by length of scratch buffer, STP_BUF_LEN.<p>
+Like printf, except output goes into a global scratch buffer which will contain the null-terminated output. Safe because overflowing the buffer is not allowed. Size is limited by length of scratch buffer, STP_BUF_LEN.<p>
<dl compact><dt><b>Parameters:</b></dt><dd>
<table border="0" cellspacing="2" cellpadding="0">
<tr><td valign="top"></td><td valign="top"><em>fmt</em>&nbsp;</td><td>A printf-style format string followed by a variable number of args. </td></tr>
@@ -112,11 +110,51 @@ Like printf, except output goes into <a class="el" href="group__scbuf.html#ga0">
<dl compact><dt><b>See also:</b></dt><dd><a class="el" href="group__scbuf.html#ga4">_stp_scbuf_clear</a> </dd></dl>
<p>
-Definition at line <a class="el" href="scbuf_8c-source.html#l00032">32</a> of file <a class="el" href="scbuf_8c-source.html">scbuf.c</a>.
+Definition at line <a class="el" href="scbuf_8c-source.html#l00034">34</a> of file <a class="el" href="scbuf_8c-source.html">scbuf.c</a>.
<p>
-References <a class="el" href="scbuf_8c-source.html#l00018">_stp_scbuf</a>, and <a class="el" href="scbuf_8c-source.html#l00015">STP_BUF_LEN</a>.
+References <a class="el" href="scbuf_8c-source.html#l00017">STP_BUF_LEN</a>.
<p>
Referenced by <a class="el" href="sym_8c-source.html#l00045">_stp_symbol_sprint()</a>. </td>
</tr>
</table>
+<a class="anchor" name="ga3" doxytag="scbuf.c::_stp_sprint_str"></a><p>
+<table class="mdTable" cellpadding="2" cellspacing="0">
+ <tr>
+ <td class="mdRow">
+ <table cellpadding="0" cellspacing="0" border="0">
+ <tr>
+ <td class="md" nowrap valign="top">void _stp_sprint_str </td>
+ <td class="md" valign="top">(&nbsp;</td>
+ <td class="md" nowrap valign="top">const char *&nbsp;</td>
+ <td class="mdname1" valign="top" nowrap> <em>str</em> </td>
+ <td class="md" valign="top">&nbsp;)&nbsp;</td>
+ <td class="md" nowrap></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+</table>
+<table cellspacing="5" cellpadding="0" border="0">
+ <tr>
+ <td>
+ &nbsp;
+ </td>
+ <td>
+
+<p>
+Write a string into the scratch buffer.
+<p>
+Copies a string into a global scratch buffer. Safe because overflowing the buffer is not allowed. Size is limited by length of scratch buffer, STP_BUF_LEN. This is more efficient than using <a class="el" href="group__scbuf.html#ga2">_stp_sprint()</a>.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+ <table border="0" cellspacing="2" cellpadding="0">
+ <tr><td valign="top"></td><td valign="top"><em>str</em>&nbsp;</td><td>A string. </td></tr>
+ </table>
+</dl>
+
+<p>
+Definition at line <a class="el" href="scbuf_8c-source.html#l00056">56</a> of file <a class="el" href="scbuf_8c-source.html">scbuf.c</a>.
+<p>
+References <a class="el" href="scbuf_8c-source.html#l00017">STP_BUF_LEN</a>. </td>
+ </tr>
+</table>
</body></html>
diff --git a/runtime/docs/html/group__stack.html b/runtime/docs/html/group__stack.html
index bf112d0a..159a529f 100644
--- a/runtime/docs/html/group__stack.html
+++ b/runtime/docs/html/group__stack.html
@@ -8,11 +8,58 @@
<h1>Stack Tracing Functions</h1><table border="0" cellpadding="0" cellspacing="0">
<tr><td></td></tr>
<tr><td colspan="2"><br><h2>Functions</h2></td></tr>
-<tr><td class="memItemLeft" nowrap align="right" valign="top"><a class="anchor" name="ga6" doxytag="stack::_stp_stack_print"></a>
-void&nbsp;</td><td class="memItemRight" valign="bottom"><b>_stp_stack_print</b> (int verbose, int levels)</td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">void&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__stack.html#ga6">_stp_stack_print</a> (int verbose, int levels)</td></tr>
+<tr><td class="mdescLeft">&nbsp;</td><td class="mdescRight">Print stack dump. <a href="#ga6"></a><br></td></tr>
<tr><td class="memItemLeft" nowrap align="right" valign="top"><a class="anchor" name="ga7" doxytag="stack::_stp_stack_sprint"></a>
char *&nbsp;</td><td class="memItemRight" valign="bottom"><b>_stp_stack_sprint</b> (int verbose, int levels)</td></tr>
</table>
+<hr><h2>Function Documentation</h2>
+<a class="anchor" name="ga6" doxytag="stack.c::_stp_stack_print"></a><p>
+<table class="mdTable" cellpadding="2" cellspacing="0">
+ <tr>
+ <td class="mdRow">
+ <table cellpadding="0" cellspacing="0" border="0">
+ <tr>
+ <td class="md" nowrap valign="top">void _stp_stack_print </td>
+ <td class="md" valign="top">(&nbsp;</td>
+ <td class="md" nowrap valign="top">int&nbsp;</td>
+ <td class="mdname" nowrap> <em>verbose</em>, </td>
+ </tr>
+ <tr>
+ <td class="md" nowrap align="right"></td>
+ <td class="md"></td>
+ <td class="md" nowrap>int&nbsp;</td>
+ <td class="mdname" nowrap> <em>levels</em></td>
+ </tr>
+ <tr>
+ <td class="md"></td>
+ <td class="md">)&nbsp;</td>
+ <td class="md" colspan="2"></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+</table>
+<table cellspacing="5" cellpadding="0" border="0">
+ <tr>
+ <td>
+ &nbsp;
+ </td>
+ <td>
+
+<p>
+Print stack dump.
+<p>
+Prints a stack dump to the trace buffer. <dl compact><dt><b>Parameters:</b></dt><dd>
+ <table border="0" cellspacing="2" cellpadding="0">
+ <tr><td valign="top"></td><td valign="top"><em>verbose</em>&nbsp;</td><td>Verbosity: </td></tr>
+ </table>
+</dl>
+
+<p>
+Definition at line <a class="el" href="stack_8c-source.html#l00151">151</a> of file <a class="el" href="stack_8c-source.html">stack.c</a>. </td>
+ </tr>
+</table>
</body></html>
diff --git a/runtime/docs/html/group__sym.html b/runtime/docs/html/group__sym.html
index c5301602..eafa03c2 100644
--- a/runtime/docs/html/group__sym.html
+++ b/runtime/docs/html/group__sym.html
@@ -98,7 +98,7 @@ Uses scbuf. </dd></dl>
<p>
Definition at line <a class="el" href="sym_8c-source.html#l00045">45</a> of file <a class="el" href="sym_8c-source.html">sym.c</a>.
<p>
-References <a class="el" href="scbuf_8c-source.html#l00032">_stp_sprint()</a>. </td>
+References <a class="el" href="scbuf_8c-source.html#l00034">_stp_sprint()</a>. </td>
</tr>
</table>
</body></html>
diff --git a/runtime/docs/html/kprobe__where__funct_8c-source.html b/runtime/docs/html/kprobe__where__funct_8c-source.html
index dbe9ac74..7da50eda 100644
--- a/runtime/docs/html/kprobe__where__funct_8c-source.html
+++ b/runtime/docs/html/kprobe__where__funct_8c-source.html
@@ -78,9 +78,9 @@
00069
00070 <span class="comment">/* now walk the hash table and print out all the information */</span>
00071 <a class="code" href="group__maps.html#ga31">foreach</a>(funct_locations, ptr) {
-00072 <a class="code" href="group__scbuf.html#ga4">_stp_scbuf_clear</a>();
+00072 <span class="keywordtype">char</span> *str =<a class="code" href="group__scbuf.html#ga4">_stp_scbuf_clear</a>();
00073 <a class="code" href="group__sym.html#ga2">_stp_symbol_sprint</a> (<a class="code" href="group__maps.html#ga26">key1int</a>(ptr));
-00074 <a class="code" href="group__io.html#ga0">dlog</a>(<span class="stringliteral">"%lld\t0x%p\t(%s)\n"</span>, ptr-&gt;<a class="code" href="structmap__node__int64.html#o1">val</a>, <a class="code" href="group__maps.html#ga26">key1int</a>(ptr), _stp_scbuf);
+00074 <a class="code" href="group__io.html#ga0">dlog</a>(<span class="stringliteral">"%lld\t0x%p\t(%s)\n"</span>, ptr-&gt;<a class="code" href="structmap__node__int64.html#o1">val</a>, <a class="code" href="group__maps.html#ga26">key1int</a>(ptr), str);
00075 }
00076
00077 <a class="code" href="group__maps.html#ga7">_stp_map_del</a>(funct_locations);
diff --git a/runtime/docs/html/scbuf_8c-source.html b/runtime/docs/html/scbuf_8c-source.html
index a9e9ddc8..ffdfaac7 100644
--- a/runtime/docs/html/scbuf_8c-source.html
+++ b/runtime/docs/html/scbuf_8c-source.html
@@ -5,75 +5,99 @@
</head><body>
<!-- Generated by Doxygen 1.4.1 -->
<div class="qindex"><a class="qindex" href="index.html">Main&nbsp;Page</a> | <a class="qindex" href="modules.html">Modules</a> | <a class="qindex" href="annotated.html">Data&nbsp;Structures</a> | <a class="qindex" href="dirs.html">Directories</a> | <a class="qindex" href="files.html">File&nbsp;List</a> | <a class="qindex" href="functions.html">Data&nbsp;Fields</a> | <a class="qindex" href="globals.html">Globals</a> | <a class="qindex" href="pages.html">Related&nbsp;Pages</a></div>
-<h1>scbuf.c</h1><div class="fragment"><pre class="fragment">00001 <span class="preprocessor">#ifndef _SCBUF_C_</span>
-00002 <span class="preprocessor"></span><span class="preprocessor">#define _SCBUF_C_</span>
+<h1>scbuf.c</h1><div class="fragment"><pre class="fragment">00001 <span class="preprocessor">#ifndef _SCBUF_C_ </span><span class="comment">/* -*- linux-c -*- */</span>
+00002 <span class="preprocessor">#define _SCBUF_C_</span>
00003 <span class="preprocessor"></span>
-00004 <span class="comment">/* -*- linux-c -*- */</span><span class="comment"></span>
-00005 <span class="comment">/** @file scbuf.c</span>
-00006 <span class="comment"> * @addtogroup scbuf Scratch Buffer</span>
-00007 <span class="comment"> * Scratch Buffer Functions.</span>
-00008 <span class="comment"> * The scratch buffer is for collecting output before storing in a map,</span>
-00009 <span class="comment"> * printing, etc. This is a per-cpu static buffer. It is necessary because </span>
-00010 <span class="comment"> * of the limited stack space available in the kernel.</span>
-00011 <span class="comment"> * @{</span>
-00012 <span class="comment"> */</span>
-00013 <span class="comment"></span>
-00014 <span class="comment">/** Maximum size of buffer, not including terminating NULL */</span>
-<a name="l00015"></a><a class="code" href="group__scbuf.html#ga6">00015</a> <span class="preprocessor">#define STP_BUF_LEN 8191</span>
-00016 <span class="preprocessor"></span><span class="comment"></span>
-00017 <span class="comment">/** Scratch buffer for printing, building strings, etc */</span>
-<a name="l00018"></a><a class="code" href="group__scbuf.html#ga0">00018</a> <span class="keywordtype">char</span> <a class="code" href="group__scbuf.html#ga0">_stp_scbuf</a>[<a class="code" href="group__scbuf.html#ga6">STP_BUF_LEN</a>+1];
-00019 <span class="keyword">static</span> <span class="keywordtype">int</span> _stp_scbuf_len = <a class="code" href="group__scbuf.html#ga6">STP_BUF_LEN</a>;
-00020 <span class="comment"></span>
-00021 <span class="comment">/** Sprint into the scratch buffer.</span>
-00022 <span class="comment"> * Like printf, except output goes into #_stp_scbuf,</span>
-00023 <span class="comment"> * which will contain the null-terminated output.</span>
-00024 <span class="comment"> * Safe because overflowing #_stp_scbuf is not allowed.</span>
-00025 <span class="comment"> * Size is limited by length of scratch buffer, STP_BUF_LEN.</span>
-00026 <span class="comment"> *</span>
-00027 <span class="comment"> * @param fmt A printf-style format string followed by a </span>
-00028 <span class="comment"> * variable number of args.</span>
-00029 <span class="comment"> * @sa _stp_scbuf_clear</span>
-00030 <span class="comment"> */</span>
-00031
-<a name="l00032"></a><a class="code" href="group__scbuf.html#ga2">00032</a> <span class="keywordtype">void</span> <a class="code" href="group__scbuf.html#ga2">_stp_sprint</a> (<span class="keyword">const</span> <span class="keywordtype">char</span> *fmt, ...)
-00033 {
-00034 <span class="keywordtype">int</span> num;
-00035 va_list args;
-00036 <span class="keywordtype">char</span> *buf = <a class="code" href="group__scbuf.html#ga0">_stp_scbuf</a> + <a class="code" href="group__scbuf.html#ga6">STP_BUF_LEN</a> - _stp_scbuf_len;
-00037 va_start(args, fmt);
-00038 num = vscnprintf(buf, _stp_scbuf_len, fmt, args);
-00039 va_end(args);
-00040 <span class="keywordflow">if</span> (num &gt; 0)
-00041 _stp_scbuf_len -= num;
-00042 }
-00043
-00044 <span class="keywordtype">void</span> _stp_sprint_str (<span class="keyword">const</span> <span class="keywordtype">char</span> *str)
-00045 {
-00046 <span class="keywordtype">char</span> *buf = <a class="code" href="group__scbuf.html#ga0">_stp_scbuf</a> + <a class="code" href="group__scbuf.html#ga6">STP_BUF_LEN</a> - _stp_scbuf_len;
-00047 <span class="keywordtype">int</span> num = strlen (str);
-00048 <span class="keywordflow">if</span> (num &gt; _stp_scbuf_len)
-00049 num = _stp_scbuf_len;
-00050 strncpy (buf, str, num);
-00051 _stp_scbuf_len -= num;
-00052 }
-00053 <span class="comment"></span>
-00054 <span class="comment">/** Clear the scratch buffer.</span>
-00055 <span class="comment"> * Output from _stp_sprint() will accumulate in the buffer</span>
-00056 <span class="comment"> * until this is called.</span>
-00057 <span class="comment"> */</span>
-00058
-<a name="l00059"></a><a class="code" href="group__scbuf.html#ga4">00059</a> <span class="keywordtype">void</span> _stp_scbuf_clear (<span class="keywordtype">void</span>)
-00060 {
-00061 _stp_scbuf_len = <a class="code" href="group__scbuf.html#ga6">STP_BUF_LEN</a>;
-00062 <a class="code" href="group__scbuf.html#ga0">_stp_scbuf</a>[0] = 0;
-00063 }
-00064
-00065 <span class="keyword">static</span> <span class="keywordtype">char</span> *_stp_scbuf_cur (<span class="keywordtype">void</span>)
-00066 {
-00067 <span class="keywordflow">return</span> <a class="code" href="group__scbuf.html#ga0">_stp_scbuf</a> + <a class="code" href="group__scbuf.html#ga6">STP_BUF_LEN</a> - _stp_scbuf_len;
-00068 }
-00069 <span class="comment"></span>
-00070 <span class="comment">/** @} */</span>
-00071 <span class="preprocessor">#endif </span><span class="comment">/* _SCBUF_C_ */</span>
+00004 <span class="preprocessor">#include &lt;linux/config.h&gt;</span>
+00005 <span class="comment"></span>
+00006 <span class="comment">/** @file scbuf.c</span>
+00007 <span class="comment"> * @addtogroup scbuf Scratch Buffer</span>
+00008 <span class="comment"> * Scratch Buffer Functions.</span>
+00009 <span class="comment"> * The scratch buffer is for collecting output before storing in a map,</span>
+00010 <span class="comment"> * printing, etc. This is a per-cpu static buffer. It is necessary because </span>
+00011 <span class="comment"> * of the limited stack space available in the kernel.</span>
+00012 <span class="comment"> * @todo Need careful review of these to insure safety.</span>
+00013 <span class="comment"> * @{</span>
+00014 <span class="comment"> */</span>
+00015 <span class="comment"></span>
+00016 <span class="comment">/** Maximum size of buffer, not including terminating NULL */</span>
+<a name="l00017"></a><a class="code" href="group__scbuf.html#ga6">00017</a> <span class="preprocessor">#define STP_BUF_LEN 8191</span>
+00018 <span class="preprocessor"></span><span class="comment"></span>
+00019 <span class="comment">/** Scratch buffer for printing, building strings, etc */</span>
+00020 <span class="keyword">static</span> <span class="keywordtype">char</span> _stp_scbuf[NR_CPUS][<a class="code" href="group__scbuf.html#ga6">STP_BUF_LEN</a>+1];
+00021 <span class="keyword">static</span> <span class="keywordtype">int</span> _stp_scbuf_len[NR_CPUS];
+00022 <span class="comment"></span>
+00023 <span class="comment">/** Sprint into the scratch buffer.</span>
+00024 <span class="comment"> * Like printf, except output goes into a global scratch buffer</span>
+00025 <span class="comment"> * which will contain the null-terminated output.</span>
+00026 <span class="comment"> * Safe because overflowing the buffer is not allowed.</span>
+00027 <span class="comment"> * Size is limited by length of scratch buffer, STP_BUF_LEN.</span>
+00028 <span class="comment"> *</span>
+00029 <span class="comment"> * @param fmt A printf-style format string followed by a </span>
+00030 <span class="comment"> * variable number of args.</span>
+00031 <span class="comment"> * @sa _stp_scbuf_clear</span>
+00032 <span class="comment"> */</span>
+00033
+<a name="l00034"></a><a class="code" href="group__scbuf.html#ga2">00034</a> <span class="keywordtype">void</span> <a class="code" href="group__scbuf.html#ga2">_stp_sprint</a> (<span class="keyword">const</span> <span class="keywordtype">char</span> *fmt, ...)
+00035 {
+00036 <span class="keywordtype">int</span> num;
+00037 va_list args;
+00038 <span class="keywordtype">int</span> cpu = smp_processor_id();
+00039 <span class="keywordtype">char</span> *buf = _stp_scbuf[cpu] + <a class="code" href="group__scbuf.html#ga6">STP_BUF_LEN</a> - _stp_scbuf_len[cpu];
+00040 va_start(args, fmt);
+00041 num = vscnprintf(buf, _stp_scbuf_len[cpu], fmt, args);
+00042 va_end(args);
+00043 <span class="keywordflow">if</span> (num &gt; 0)
+00044 _stp_scbuf_len[cpu] -= num;
+00045 }
+00046 <span class="comment"></span>
+00047 <span class="comment">/** Write a string into the scratch buffer.</span>
+00048 <span class="comment"> * Copies a string into a global scratch buffer.</span>
+00049 <span class="comment"> * Safe because overflowing the buffer is not allowed.</span>
+00050 <span class="comment"> * Size is limited by length of scratch buffer, STP_BUF_LEN.</span>
+00051 <span class="comment"> * This is more efficient than using _stp_sprint().</span>
+00052 <span class="comment"> *</span>
+00053 <span class="comment"> * @param str A string.</span>
+00054 <span class="comment"> */</span>
+00055
+<a name="l00056"></a><a class="code" href="group__scbuf.html#ga3">00056</a> <span class="keywordtype">void</span> <a class="code" href="group__scbuf.html#ga3">_stp_sprint_str</a> (<span class="keyword">const</span> <span class="keywordtype">char</span> *str)
+00057 {
+00058 <span class="keywordtype">int</span> cpu = smp_processor_id();
+00059 <span class="keywordtype">char</span> *buf = _stp_scbuf[cpu] + <a class="code" href="group__scbuf.html#ga6">STP_BUF_LEN</a> - _stp_scbuf_len[cpu];
+00060 <span class="keywordtype">int</span> num = strlen (str);
+00061 <span class="keywordflow">if</span> (num &gt; _stp_scbuf_len[cpu])
+00062 num = _stp_scbuf_len[cpu];
+00063 strncpy (buf, str, num);
+00064 _stp_scbuf_len[cpu] -= num;
+00065 }
+00066 <span class="comment"></span>
+00067 <span class="comment">/** Clear the scratch buffer.</span>
+00068 <span class="comment"> * This function should be called before anything is written to </span>
+00069 <span class="comment"> * the scratch buffer. Output will accumulate in the buffer</span>
+00070 <span class="comment"> * until this function is called again. </span>
+00071 <span class="comment"> * @returns A pointer to the buffer.</span>
+00072 <span class="comment"> */</span>
+00073
+<a name="l00074"></a><a class="code" href="group__scbuf.html#ga4">00074</a> <span class="keywordtype">char</span> *<a class="code" href="group__scbuf.html#ga4">_stp_scbuf_clear</a> (<span class="keywordtype">void</span>)
+00075 {
+00076 <span class="keywordtype">int</span> cpu = smp_processor_id();
+00077 _stp_scbuf_len[cpu] = <a class="code" href="group__scbuf.html#ga6">STP_BUF_LEN</a>;
+00078 *_stp_scbuf[cpu] = 0;
+00079 <span class="keywordflow">return</span> _stp_scbuf[cpu];
+00080 }
+00081 <span class="comment"></span>
+00082 <span class="comment">/** Get the current top of the scratch buffer.</span>
+00083 <span class="comment"> * This returns the address of the location where</span>
+00084 <span class="comment"> * data will be written next in the scratch buffer.</span>
+00085 <span class="comment"> * @returns A pointer</span>
+00086 <span class="comment"> */</span>
+00087
+00088 <span class="keyword">static</span> <span class="keywordtype">char</span> *_stp_scbuf_cur (<span class="keywordtype">void</span>)
+00089 {
+00090 <span class="keywordtype">int</span> cpu = smp_processor_id();
+00091 <span class="keywordflow">return</span> _stp_scbuf[cpu] + <a class="code" href="group__scbuf.html#ga6">STP_BUF_LEN</a> - _stp_scbuf_len[cpu];
+00092 }
+00093 <span class="comment"></span>
+00094 <span class="comment">/** @} */</span>
+00095 <span class="preprocessor">#endif </span><span class="comment">/* _SCBUF_C_ */</span>
</pre></div></body></html>
diff --git a/runtime/docs/html/stack_8c-source.html b/runtime/docs/html/stack_8c-source.html
index f4df8413..552f51cc 100644
--- a/runtime/docs/html/stack_8c-source.html
+++ b/runtime/docs/html/stack_8c-source.html
@@ -103,7 +103,7 @@
00096 <span class="preprocessor"></span> <span class="keywordflow">while</span> (valid_stack_ptr(tinfo, (<span class="keywordtype">void</span> *)ebp)) {
00097 addr = *(<span class="keywordtype">unsigned</span> <span class="keywordtype">long</span> *)(ebp + 4);
00098 <a class="code" href="group__sym.html#ga2">_stp_symbol_sprint</a> (addr);
-00099 _stp_sprint_str(<span class="stringliteral">"\n"</span>);
+00099 <a class="code" href="group__scbuf.html#ga3">_stp_sprint_str</a>(<span class="stringliteral">"\n"</span>);
00100 ebp = *(<span class="keywordtype">unsigned</span> <span class="keywordtype">long</span> *)ebp;
00101 }
00102 <span class="preprocessor">#else</span>
@@ -111,7 +111,7 @@
00104 addr = *stack++;
00105 <span class="keywordflow">if</span> (_stp_kta (addr)) {
00106 <a class="code" href="group__sym.html#ga2">_stp_symbol_sprint</a> (addr);
-00107 _stp_sprint_str (<span class="stringliteral">"\n"</span>);
+00107 <a class="code" href="group__scbuf.html#ga3">_stp_sprint_str</a> (<span class="stringliteral">"\n"</span>);
00108 }
00109 }
00110 <span class="preprocessor">#endif</span>
@@ -149,21 +149,26 @@
00142 }
00143
00144 <span class="preprocessor">#endif </span><span class="comment">/* i386 */</span>
-00145
-00146 <span class="keywordtype">void</span> _stp_stack_print (<span class="keywordtype">int</span> verbose, <span class="keywordtype">int</span> levels)
-00147 {
-00148 <span class="keywordtype">unsigned</span> <span class="keywordtype">long</span> stack;
-00149 <span class="keywordflow">return</span> __stp_stack_print (&amp;stack, verbose, levels);
-00150 }
-00151
-00152 <span class="keywordtype">char</span> *_stp_stack_sprint (<span class="keywordtype">int</span> verbose, <span class="keywordtype">int</span> levels)
-00153 {
-00154 <span class="keywordtype">unsigned</span> <span class="keywordtype">long</span> stack;
-00155 <span class="keywordtype">char</span> *ptr = _stp_scbuf_cur();
-00156 __stp_stack_sprint (&amp;stack, verbose, levels);
-00157 <span class="keywordflow">return</span> ptr;
-00158 }
-00159 <span class="comment"></span>
-00160 <span class="comment">/** @} */</span>
-00161 <span class="preprocessor">#endif </span><span class="comment">/* _STACK_C_ */</span>
+00145 <span class="comment"></span>
+00146 <span class="comment">/** Print stack dump.</span>
+00147 <span class="comment"> * Prints a stack dump to the trace buffer.</span>
+00148 <span class="comment"> * @param verbose Verbosity:</span>
+00149 <span class="comment"> */</span>
+00150
+<a name="l00151"></a><a class="code" href="group__stack.html#ga6">00151</a> <span class="keywordtype">void</span> <a class="code" href="group__stack.html#ga6">_stp_stack_print</a> (<span class="keywordtype">int</span> verbose, <span class="keywordtype">int</span> levels)
+00152 {
+00153 <span class="keywordtype">unsigned</span> <span class="keywordtype">long</span> stack;
+00154 <span class="keywordflow">return</span> __stp_stack_print (&amp;stack, verbose, levels);
+00155 }
+00156
+00157 <span class="keywordtype">char</span> *_stp_stack_sprint (<span class="keywordtype">int</span> verbose, <span class="keywordtype">int</span> levels)
+00158 {
+00159 <span class="keywordtype">unsigned</span> <span class="keywordtype">long</span> stack;
+00160 <span class="keywordtype">char</span> *ptr = _stp_scbuf_cur();
+00161 __stp_stack_sprint (&amp;stack, verbose, levels);
+00162 <span class="keywordflow">return</span> ptr;
+00163 }
+00164 <span class="comment"></span>
+00165 <span class="comment">/** @} */</span>
+00166 <span class="preprocessor">#endif </span><span class="comment">/* _STACK_C_ */</span>
</pre></div></body></html>
diff --git a/runtime/docs/html/stack_8c.html b/runtime/docs/html/stack_8c.html
index 65cb258e..093cc186 100644
--- a/runtime/docs/html/stack_8c.html
+++ b/runtime/docs/html/stack_8c.html
@@ -13,9 +13,9 @@
<a href="stack_8c-source.html">Go to the source code of this file.</a><table border="0" cellpadding="0" cellspacing="0">
<tr><td></td></tr>
<tr><td colspan="2"><br><h2>Functions</h2></td></tr>
-<tr><td class="memItemLeft" nowrap align="right" valign="top"><a class="anchor" name="ga6" doxytag="stack.c::_stp_stack_print"></a>
-void&nbsp;</td><td class="memItemRight" valign="bottom"><b>_stp_stack_print</b> (int verbose, int levels)</td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">void&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__stack.html#ga6">_stp_stack_print</a> (int verbose, int levels)</td></tr>
+<tr><td class="mdescLeft">&nbsp;</td><td class="mdescRight">Print stack dump. <a href="group__stack.html#ga6"></a><br></td></tr>
<tr><td class="memItemLeft" nowrap align="right" valign="top"><a class="anchor" name="ga7" doxytag="stack.c::_stp_stack_sprint"></a>
char *&nbsp;</td><td class="memItemRight" valign="bottom"><b>_stp_stack_sprint</b> (int verbose, int levels)</td></tr>
diff --git a/runtime/docs/html/structmap__root.html b/runtime/docs/html/structmap__root.html
index d6e95bd6..2a4eb819 100644
--- a/runtime/docs/html/structmap__root.html
+++ b/runtime/docs/html/structmap__root.html
@@ -99,7 +99,7 @@ Definition at line <a class="el" href="map_8h-source.html#l00067">67</a> of file
<p>
this is the creation data saved between the key functions and the set/get functions
<p>
-<dl compact><dt><b><a class="el" href="todo.html#_todo000008">Todo:</a></b></dt><dd>Needs to be per-cpu data for SMP support</dd></dl>
+<dl compact><dt><b><a class="el" href="todo.html#_todo000009">Todo:</a></b></dt><dd>Needs to be per-cpu data for SMP support</dd></dl>
<p>
Definition at line <a class="el" href="map_8h-source.html#l00093">93</a> of file <a class="el" href="map_8h-source.html">map.h</a>.
diff --git a/runtime/docs/html/test4_2dtr_8c-source.html b/runtime/docs/html/test4_2dtr_8c-source.html
index 2847b88a..af19db1a 100644
--- a/runtime/docs/html/test4_2dtr_8c-source.html
+++ b/runtime/docs/html/test4_2dtr_8c-source.html
@@ -13,138 +13,251 @@
00004 <span class="preprocessor">#include &lt;linux/module.h&gt;</span>
00005 <span class="preprocessor">#include &lt;linux/interrupt.h&gt;</span>
00006 <span class="preprocessor">#include &lt;net/sock.h&gt;</span>
-00007
-00008 <span class="preprocessor">#include "<a class="code" href="runtime_8h.html">runtime.h</a>"</span>
-00009 <span class="preprocessor">#include "<a class="code" href="io_8c.html">io.c</a>"</span>
-00010 <span class="preprocessor">#include "<a class="code" href="map_8c.html">map.c</a>"</span>
-00011 <span class="preprocessor">#include "<a class="code" href="probes_8c.html">probes.c</a>"</span>
-00012 <span class="preprocessor">#include "<a class="code" href="stack_8c.html">stack.c</a>"</span>
-00013
-00014 MODULE_DESCRIPTION(<span class="stringliteral">"SystemTap probe: test4"</span>);
-00015 MODULE_AUTHOR(<span class="stringliteral">"Martin Hunt &lt;hunt@redhat.com&gt;"</span>);
-00016
-00017 <span class="keyword">static</span> <span class="keywordtype">char</span> tbuffer[2][50000];
-00018 <span class="keyword">static</span> <span class="keywordtype">void</span> stp_helper(<span class="keywordtype">void</span> *);
-00019 <span class="keyword">static</span> DECLARE_WORK(stp_work, stp_helper, tbuffer);
-00020
-00021 <a class="code" href="structmap__root.html">MAP</a> opens, reads, writes, traces;
-00022 <span class="keyword">static</span> <span class="keywordtype">int</span> bufcount = 0;
-00023
-00024 <span class="keyword">static</span> <span class="keywordtype">void</span> stp_helper (<span class="keywordtype">void</span> *data)
-00025 {
-00026 <a class="code" href="group__io.html#ga0">dlog</a> (<span class="stringliteral">"HELPER\n"</span>);
-00027 }
+00007 <span class="preprocessor">#include &lt;linux/netlink.h&gt;</span>
+00008
+00009 <span class="preprocessor">#include "<a class="code" href="runtime_8h.html">runtime.h</a>"</span>
+00010 <span class="preprocessor">#include "<a class="code" href="io_8c.html">io.c</a>"</span>
+00011 <span class="preprocessor">#include "<a class="code" href="map_8c.html">map.c</a>"</span>
+00012 <span class="preprocessor">#include "<a class="code" href="probes_8c.html">probes.c</a>"</span>
+00013 <span class="preprocessor">#include "<a class="code" href="stack_8c.html">stack.c</a>"</span>
+00014
+00015 MODULE_DESCRIPTION(<span class="stringliteral">"SystemTap probe: test4"</span>);
+00016 MODULE_AUTHOR(<span class="stringliteral">"Martin Hunt &lt;hunt@redhat.com&gt;"</span>);
+00017
+00018 <span class="keyword">static</span> <span class="keywordtype">char</span> tbuffer[2][50000];
+00019 <span class="keyword">static</span> <span class="keywordtype">void</span> stp_helper(<span class="keywordtype">void</span> *);
+00020 <span class="keyword">static</span> DECLARE_WORK(stp_work, stp_helper, tbuffer);
+00021
+00022 <a class="code" href="structmap__root.html">MAP</a> opens, reads, writes, traces;
+00023 <span class="keyword">static</span> <span class="keywordtype">int</span> bufcount = 0;
+00024
+00025 <span class="comment">/* netlink control channel */</span>
+00026 <span class="keyword">static</span> <span class="keyword">struct </span>sock *control;
+00027 <span class="keyword">static</span> <span class="keywordtype">int</span> seq = 0;
00028
-00029 asmlinkage <span class="keywordtype">long</span> inst_sys_open (<span class="keyword">const</span> <span class="keywordtype">char</span> __user * filename, <span class="keywordtype">int</span> flags, <span class="keywordtype">int</span> mode)
-00030 {
-00031 <a class="code" href="group__maps.html#ga12">_stp_map_key_str</a> (opens, current-&gt;comm);
-00032 <a class="code" href="group__maps.html#ga17">_stp_map_add_int64</a> (opens, 1);
-00033 jprobe_return();
-00034 <span class="keywordflow">return</span> 0;
-00035 }
-00036
-00037 asmlinkage ssize_t inst_sys_read (<span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> fd, <span class="keywordtype">char</span> __user * buf, size_t count)
-00038 {
-00039 <a class="code" href="group__maps.html#ga12">_stp_map_key_str</a> (reads, current-&gt;comm);
-00040 <a class="code" href="group__maps.html#ga23">_stp_map_stat_add</a> (reads, count);
-00041 jprobe_return();
-00042 <span class="keywordflow">return</span> 0;
-00043 }
-00044
-00045 asmlinkage ssize_t inst_sys_write (<span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> fd, <span class="keyword">const</span> <span class="keywordtype">char</span> __user * buf, size_t count)
-00046 {
-00047 <a class="code" href="group__maps.html#ga12">_stp_map_key_str</a> (writes, current-&gt;comm);
-00048 <a class="code" href="group__maps.html#ga23">_stp_map_stat_add</a> (writes, count);
-00049 jprobe_return();
-00050 <span class="keywordflow">return</span> 0;
-00051 }
+00029 <span class="keywordtype">int</span> pid;
+00030 <span class="comment">/*</span>
+00031 <span class="comment"> * send_reply - send reply to userspace over netlink control channel</span>
+00032 <span class="comment"> */</span>
+00033 <span class="keyword">static</span> <span class="keywordtype">int</span> send_reply(<span class="keywordtype">int</span> type, <span class="keywordtype">void</span> *reply, <span class="keywordtype">int</span> len, <span class="keywordtype">int</span> pid)
+00034 {
+00035 <span class="keyword">struct </span>sk_buff *skb;
+00036 <span class="keyword">struct </span>nlmsghdr *nlh;
+00037 <span class="keywordtype">void</span> *data;
+00038 <span class="keywordtype">int</span> size;
+00039 <span class="keywordtype">int</span> err;
+00040
+00041 size = NLMSG_SPACE(len);
+00042 skb = alloc_skb(size, GFP_KERNEL);
+00043 <span class="keywordflow">if</span> (!skb)
+00044 return -1;
+00045 nlh = NLMSG_PUT(skb, pid, seq++, type, size - sizeof(*nlh));
+00046 nlh-&gt;nlmsg_flags = 0;
+00047 data = NLMSG_DATA(nlh);
+00048 memcpy(data, reply, len);
+00049 err = netlink_unicast(control, skb, pid, MSG_DONTWAIT);
+00050
+00051 return 0;
00052
-00053 <span class="keywordtype">int</span> inst_show_cpuinfo(<span class="keyword">struct</span> seq_file *m, <span class="keywordtype">void</span> *v)
-00054 {
-00055 _stp_stack_print (0,0);
-00056 _stp_stack_print (1,0);
-00057
-00058 <a class="code" href="group__scbuf.html#ga4">_stp_scbuf_clear</a>();
-00059 <a class="code" href="group__lists.html#ga5">_stp_list_add</a> (traces, _stp_stack_sprint(0,0));
-00060 <span class="keywordflow">if</span> (bufcount++ == 0)
-00061 schedule_work (&amp;stp_work);
-00062
-00063 jprobe_return();
-00064 return 0;
-00065 }
-00066
-00067
-00068 static struct jprobe dtr_probes[] = {
-00069 {
-00070 .kp.addr = (kprobe_opcode_t *)<span class="stringliteral">"sys_open"</span>,
-00071 .entry = (kprobe_opcode_t *) inst_sys_open
-00072 },
-00073 {
-00074 .kp.addr = (kprobe_opcode_t *)<span class="stringliteral">"sys_read"</span>,
-00075 .entry = (kprobe_opcode_t *) inst_sys_read
-00076 },
-00077 {
-00078 .kp.addr = (kprobe_opcode_t *)<span class="stringliteral">"sys_write"</span>,
-00079 .entry = (kprobe_opcode_t *) inst_sys_write
-00080 },
-00081 {
-00082 .kp.addr = (kprobe_opcode_t *)<span class="stringliteral">"show_cpuinfo"</span>,
-00083 .entry = (kprobe_opcode_t *) inst_show_cpuinfo,
-00084 },
-00085 };
+00053 nlmsg_failure:
+00054 if (skb)
+00055 kfree_skb(skb);
+00056
+00057 return -1;
+00058 }
+00059
+00060 static <span class="keywordtype">char</span> pbuff[1024];
+00061
+00062 <span class="keywordtype">void</span> nlog (const <span class="keywordtype">char</span> *fmt, ...)
+00063 {
+00064 <span class="keywordtype">int</span> len;
+00065 va_list args;
+00066 va_start(args, fmt);
+00067 len = vscnprintf (pbuff, <span class="keyword">sizeof</span>(pbuff), fmt, args) + 1;
+00068 va_end(args);
+00069 send_reply (42, pbuff, len, pid);
+00070 }
+00071
+00072 <span class="comment">/*</span>
+00073 <span class="comment"> * msg_rcv_skb - dispatch userspace requests from netlink control channel</span>
+00074 <span class="comment"> */</span>
+00075 <span class="keyword">static</span> <span class="keywordtype">void</span> msg_rcv_skb(<span class="keyword">struct</span> sk_buff *skb)
+00076 {
+00077 <span class="keyword">struct </span>nlmsghdr *nlh = NULL;
+00078 <span class="keywordtype">int</span> flags;
+00079 <span class="keywordtype">int</span> nlmsglen, skblen;
+00080 <span class="keywordtype">void</span> *data;
+00081
+00082 skblen = skb-&gt;len;
+00083 <span class="comment">// dlog ("skblen = %d %d\n", skblen, sizeof(*nlh));</span>
+00084 <span class="keywordflow">if</span> (skblen &lt; <span class="keyword">sizeof</span> (*nlh))
+00085 return;
00086
-00087 <span class="preprocessor">#define MAX_DTR_ROUTINE (sizeof(dtr_probes)/sizeof(struct jprobe))</span>
-00088 <span class="preprocessor"></span>
-00089 <span class="keyword">static</span> <span class="keywordtype">int</span> init_dtr(<span class="keywordtype">void</span>)
-00090 {
-00091 <span class="keywordtype">int</span> ret;
-00092
-00093 opens = <a class="code" href="group__maps.html#ga2">_stp_map_new</a> (1000, INT64);
-00094 reads = <a class="code" href="group__maps.html#ga2">_stp_map_new</a> (1000, STAT);
-00095 writes = <a class="code" href="group__maps.html#ga2">_stp_map_new</a> (1000, STAT);
-00096 traces = <a class="code" href="group__lists.html#ga0">_stp_list_new</a> (1000, STRING);
-00097
-00098 ret = <a class="code" href="probes_8c.html#a2">_stp_register_jprobes</a> (dtr_probes, MAX_DTR_ROUTINE);
-00099
-00100 <a class="code" href="group__io.html#ga0">dlog</a>(<span class="stringliteral">"instrumentation is enabled...\n"</span>);
-00101 <span class="keywordflow">return</span> ret;
+00087 nlh = (struct nlmsghdr *)skb-&gt;data;
+00088 nlmsglen = nlh-&gt;nlmsg_len;
+00089
+00090 <span class="comment">// dlog ("nlmsghlen=%d\n", nlmsglen);</span>
+00091 if (nlmsglen &lt; sizeof(*nlh) || skblen &lt; nlmsglen)
+00092 return;
+00093
+00094 pid = nlh-&gt;nlmsg_pid;
+00095 flags = nlh-&gt;nlmsg_flags;
+00096
+00097 <span class="comment">// dlog ("pid=%d flags=%x %x %x %x\n", pid, flags, NLM_F_REQUEST, MSG_TRUNC, NLM_F_ACK);</span>
+00098 if (pid &lt;= 0 || !(flags &amp; NLM_F_REQUEST)) {
+00099 netlink_ack(skb, nlh, -EINVAL);
+00100 <span class="keywordflow">return</span>;
+00101 }
00102
-00103 }
-00104
-00105 <span class="keyword">static</span> <span class="keywordtype">void</span> cleanup_dtr(<span class="keywordtype">void</span>)
-00106 {
-00107 <span class="keyword">struct </span><a class="code" href="structmap__node__stat.html">map_node_stat</a> *st;
-00108 <span class="keyword">struct </span><a class="code" href="structmap__node__int64.html">map_node_int64</a> *ptr;
-00109 <span class="keyword">struct </span><a class="code" href="structmap__node__str.html">map_node_str</a> *sptr;
-00110
-00111 <a class="code" href="probes_8c.html#a1">_stp_unregister_jprobes</a> (dtr_probes, MAX_DTR_ROUTINE);
-00112
-00113 <a class="code" href="group__maps.html#ga31">foreach</a> (traces, sptr)
-00114 dlog ("trace: %s\n", sptr-&gt;str);
-00115
-00116 foreach (opens, ptr)
-00117 dlog ("opens[%s] = %lld\n", key1str(ptr), ptr-&gt;val);
-00118 dlog ("\n");
-00119
-00120 foreach (reads, st)
-00121 dlog ("reads[%s] = [count=%lld sum=%lld min=%lld max=%lld]\n", key1str(st),
-00122 st-&gt;stats.count, st-&gt;stats.sum, st-&gt;stats.min, st-&gt;stats.max);
-00123 dlog ("\n");
-00124
-00125 foreach (writes, st)
-00126 dlog ("writes[%s] = [count=%lld sum=%lld min=%lld max=%lld]\n", key1str(st),
-00127 st-&gt;stats.count, st-&gt;stats.sum, st-&gt;stats.min, st-&gt;stats.max);
-00128 dlog ("\n");
-00129
-00130 _stp_map_del (opens);
-00131 _stp_map_del (reads);
-00132 _stp_map_del (writes);
-00133
-00134 dlog("EXIT\n");
-00135 }
-00136
-00137 module_init(init_dtr);
-00138 module_exit(cleanup_dtr);
-00139 MODULE_LICENSE("GPL");
+00103 <span class="keywordflow">if</span> (flags &amp; MSG_TRUNC) {
+00104 netlink_ack(skb, nlh, -ECOMM);
+00105 <span class="keywordflow">return</span>;
+00106 }
+00107
+00108 data = NLMSG_DATA(nlh);
+00109
+00110 <span class="comment">// dlog ("NETLINK: Got message \"%s\" of type %d from pid %d\n", data, nlh-&gt;nlmsg_type,pid);</span>
+00111
+00112 <span class="keywordflow">if</span> (flags &amp; NLM_F_ACK)
+00113 netlink_ack(skb, nlh, 0);
+00114
+00115 send_reply (42, "Howdy Partner", 14, pid);
+00116 }
+00117
+00118 static <span class="keywordtype">void</span> msg_rcv(struct sock *sk, <span class="keywordtype">int</span> len)
+00119 {
+00120 <span class="keyword">struct </span>sk_buff *skb;
+00121 <a class="code" href="group__io.html#ga0">dlog</a> (<span class="stringliteral">"netlink message received\n"</span>);
+00122 <span class="keywordflow">while</span> ((skb = skb_dequeue(&amp;sk-&gt;sk_receive_queue))) {
+00123 msg_rcv_skb(skb);
+00124 kfree_skb(skb);
+00125 }
+00126 }
+00127
+00128 <span class="keyword">static</span> <span class="keywordtype">void</span> stp_helper (<span class="keywordtype">void</span> *data)
+00129 {
+00130 <a class="code" href="group__io.html#ga0">dlog</a> (<span class="stringliteral">"HELPER\n"</span>);
+00131 }
+00132
+00133 asmlinkage <span class="keywordtype">long</span> inst_sys_open (<span class="keyword">const</span> <span class="keywordtype">char</span> __user * filename, <span class="keywordtype">int</span> flags, <span class="keywordtype">int</span> mode)
+00134 {
+00135 <a class="code" href="group__maps.html#ga12">_stp_map_key_str</a> (opens, current-&gt;comm);
+00136 <a class="code" href="group__maps.html#ga17">_stp_map_add_int64</a> (opens, 1);
+00137 jprobe_return();
+00138 <span class="keywordflow">return</span> 0;
+00139 }
00140
+00141 asmlinkage ssize_t inst_sys_read (<span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> fd, <span class="keywordtype">char</span> __user * buf, size_t count)
+00142 {
+00143 <a class="code" href="group__maps.html#ga12">_stp_map_key_str</a> (reads, current-&gt;comm);
+00144 <a class="code" href="group__maps.html#ga23">_stp_map_stat_add</a> (reads, count);
+00145 jprobe_return();
+00146 <span class="keywordflow">return</span> 0;
+00147 }
+00148
+00149 asmlinkage ssize_t inst_sys_write (<span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> fd, <span class="keyword">const</span> <span class="keywordtype">char</span> __user * buf, size_t count)
+00150 {
+00151 <a class="code" href="group__maps.html#ga12">_stp_map_key_str</a> (writes, current-&gt;comm);
+00152 <a class="code" href="group__maps.html#ga23">_stp_map_stat_add</a> (writes, count);
+00153 jprobe_return();
+00154 <span class="keywordflow">return</span> 0;
+00155 }
+00156
+00157 <span class="keywordtype">int</span> inst_show_cpuinfo(<span class="keyword">struct</span> seq_file *m, <span class="keywordtype">void</span> *v)
+00158 {
+00159 <a class="code" href="group__stack.html#ga6">_stp_stack_print</a> (0,0);
+00160 <a class="code" href="group__stack.html#ga6">_stp_stack_print</a> (1,0);
+00161
+00162 <a class="code" href="group__scbuf.html#ga4">_stp_scbuf_clear</a>();
+00163 <a class="code" href="group__lists.html#ga5">_stp_list_add</a> (traces, _stp_stack_sprint(0,0));
+00164 <span class="keywordflow">if</span> (bufcount++ == 0)
+00165 schedule_work (&amp;stp_work);
+00166
+00167 jprobe_return();
+00168 return 0;
+00169 }
+00170
+00171
+00172 static struct jprobe dtr_probes[] = {
+00173 {
+00174 .kp.addr = (kprobe_opcode_t *)<span class="stringliteral">"sys_open"</span>,
+00175 .entry = (kprobe_opcode_t *) inst_sys_open
+00176 },
+00177 {
+00178 .kp.addr = (kprobe_opcode_t *)<span class="stringliteral">"sys_read"</span>,
+00179 .entry = (kprobe_opcode_t *) inst_sys_read
+00180 },
+00181 {
+00182 .kp.addr = (kprobe_opcode_t *)<span class="stringliteral">"sys_write"</span>,
+00183 .entry = (kprobe_opcode_t *) inst_sys_write
+00184 },
+00185 {
+00186 .kp.addr = (kprobe_opcode_t *)<span class="stringliteral">"show_cpuinfo"</span>,
+00187 .entry = (kprobe_opcode_t *) inst_show_cpuinfo,
+00188 },
+00189 };
+00190
+00191 <span class="preprocessor">#define MAX_DTR_ROUTINE (sizeof(dtr_probes)/sizeof(struct jprobe))</span>
+00192 <span class="preprocessor"></span>
+00193 <span class="keyword">static</span> <span class="keywordtype">int</span> init_dtr(<span class="keywordtype">void</span>)
+00194 {
+00195 <span class="keywordtype">int</span> ret;
+00196
+00197 control = netlink_kernel_create(31, msg_rcv);
+00198 <span class="keywordflow">if</span> (!control) {
+00199 <a class="code" href="group__io.html#ga0">dlog</a> (<span class="stringliteral">"Couldn't create netlink channel\n"</span>);
+00200 <span class="keywordflow">return</span> -1;
+00201 }
+00202
+00203 opens = <a class="code" href="group__maps.html#ga2">_stp_map_new</a> (1000, INT64);
+00204 reads = <a class="code" href="group__maps.html#ga2">_stp_map_new</a> (1000, STAT);
+00205 writes = <a class="code" href="group__maps.html#ga2">_stp_map_new</a> (1000, STAT);
+00206 traces = <a class="code" href="group__lists.html#ga0">_stp_list_new</a> (1000, STRING);
+00207
+00208 ret = <a class="code" href="probes_8c.html#a2">_stp_register_jprobes</a> (dtr_probes, MAX_DTR_ROUTINE);
+00209
+00210 <a class="code" href="group__io.html#ga0">dlog</a>(<span class="stringliteral">"instrumentation is enabled...\n"</span>);
+00211 <span class="keywordflow">return</span> ret;
+00212
+00213 }
+00214
+00215 <span class="keyword">static</span> <span class="keywordtype">void</span> cleanup_dtr(<span class="keywordtype">void</span>)
+00216 {
+00217 <span class="keyword">struct </span><a class="code" href="structmap__node__stat.html">map_node_stat</a> *st;
+00218 <span class="keyword">struct </span><a class="code" href="structmap__node__int64.html">map_node_int64</a> *ptr;
+00219 <span class="keyword">struct </span><a class="code" href="structmap__node__str.html">map_node_str</a> *sptr;
+00220
+00221 <a class="code" href="probes_8c.html#a1">_stp_unregister_jprobes</a> (dtr_probes, MAX_DTR_ROUTINE);
+00222
+00223 <a class="code" href="group__maps.html#ga31">foreach</a> (traces, sptr)
+00224 nlog ("trace: %s\n", sptr-&gt;str);
+00225
+00226 foreach (opens, ptr)
+00227 nlog ("opens[%s] = %lld\n", key1str(ptr), ptr-&gt;val);
+00228 nlog ("\n");
+00229
+00230 foreach (reads, st)
+00231 nlog ("reads[%s] = [count=%lld sum=%lld min=%lld max=%lld]\n", key1str(st),
+00232 st-&gt;stats.count, st-&gt;stats.sum, st-&gt;stats.min, st-&gt;stats.max);
+00233 nlog ("\n");
+00234
+00235 foreach (writes, st)
+00236 nlog ("writes[%s] = [count=%lld sum=%lld min=%lld max=%lld]\n", key1str(st),
+00237 st-&gt;stats.count, st-&gt;stats.sum, st-&gt;stats.min, st-&gt;stats.max);
+00238 nlog ("\n");
+00239
+00240 _stp_map_del (opens);
+00241 _stp_map_del (reads);
+00242 _stp_map_del (writes);
+00243
+00244 if (control)
+00245 sock_release(control-&gt;sk_socket);
+00246
+00247 dlog("EXIT\n");
+00248 }
+00249
+00250 module_init(init_dtr);
+00251 module_exit(cleanup_dtr);
+00252 MODULE_LICENSE("GPL");
+00253
</pre></div></body></html>
diff --git a/runtime/docs/html/todo.html b/runtime/docs/html/todo.html
index 7435d106..22265a06 100644
--- a/runtime/docs/html/todo.html
+++ b/runtime/docs/html/todo.html
@@ -5,7 +5,7 @@
</head><body>
<!-- Generated by Doxygen 1.4.1 -->
<div class="qindex"><a class="qindex" href="index.html">Main&nbsp;Page</a> | <a class="qindex" href="modules.html">Modules</a> | <a class="qindex" href="annotated.html">Data&nbsp;Structures</a> | <a class="qindex" href="dirs.html">Directories</a> | <a class="qindex" href="files.html">File&nbsp;List</a> | <a class="qindex" href="functions.html">Data&nbsp;Fields</a> | <a class="qindex" href="globals.html">Globals</a> | <a class="qindex" href="pages.html">Related&nbsp;Pages</a></div>
-<h1><a class="anchor" name="todo">Todo List</a></h1><a class="anchor" name="_todo000008"></a> <dl>
+<h1><a class="anchor" name="todo">Todo List</a></h1><a class="anchor" name="_todo000009"></a> <dl>
<dt>Global <a class="el" href="structmap__root.html#o7">map_root::create</a> </dt>
<dd>Needs to be per-cpu data for SMP support</dd>
</dl>
@@ -44,4 +44,9 @@
<dt>Global <a class="el" href="group__maps.html#ga23">_stp_map_stat_add</a> (MAP map, int64_t val) </dt>
<dd>Histograms don't work yet. </dd>
</dl>
+<p>
+<a class="anchor" name="_todo000008"></a> <dl>
+<dt>Group <a class="el" href="group__scbuf.html">scbuf</a> </dt>
+<dd>Need careful review of these to insure safety.</dd>
+</dl>
</body></html>
diff --git a/runtime/io.c b/runtime/io.c
index 0612f563..f5e640e9 100644
--- a/runtime/io.c
+++ b/runtime/io.c
@@ -1,7 +1,9 @@
-#ifndef _IO_C_
+#ifndef _IO_C_ /* -*- linux-c -*- */
#define _IO_C_
-/* -*- linux-c -*- */
+#include "relay-app.h"
+#include "print.c"
+
/** @file io.c
* @brief I/O functions
*/
@@ -10,6 +12,9 @@
* @{
*/
+#define STP_LOG_BUF_LEN 2047
+static char _stp_lbuf[NR_CPUS][STP_LOG_BUF_LEN + 1];
+
/** Logs Data.
* This function is compatible with printk. In fact it currently
* sends all output to vprintk, after sending "STP: ". This allows
@@ -22,44 +27,74 @@
* @todo Either deprecate or redefine this as a way to log debug or
* status messages, separate from the normal program output.
*/
-void dlog (const char *fmt, ...)
+
+void _stp_log (const char *fmt, ...)
{
- va_list args;
- printk("STP: ");
- va_start(args, fmt);
- vprintk(fmt, args);
- va_end(args);
+ int num;
+ char *buf = &_stp_lbuf[smp_processor_id()][0];
+ va_list args;
+ va_start(args, fmt);
+ num = vscnprintf (buf, STP_LOG_BUF_LEN, fmt, args);
+ va_end(args);
+ buf[num] = '\0';
+
+ if (app.logging)
+ send_reply (STP_REALTIME_DATA, buf, num + 1, stpd_pid);
+ else
+ printk("STP: %s", buf);
}
-/** Prints to the trace buffer.
- * This function uses the same formatting as printk. It currently
- * writes to the system log.
- *
- * @param fmt A variable number of args.
- * @todo Needs replaced with something much faster that does not
- * use the system log.
- */
+static void stpd_app_started(void)
+{
+ printk ("stpd has started.\n");
+}
-void _stp_print (const char *fmt, ...)
+static void stpd_app_stopped(void)
{
- va_list args;
- va_start(args, fmt);
- vprintk(fmt, args);
- va_end(args);
+ printk ("stpd has stopped.\n");
}
-/** Prints to the trace buffer.
- * This function will write a string to the trace buffer. It currently
- * writes to the system log.
- *
- * @param str String.
- * @todo Needs replaced with something much faster that does not
- * use the system log.
+static void probe_exit(void);
+
+#include <linux/delay.h>
+static int stpd_command (int type, void *data)
+{
+ if (type == STP_EXIT) {
+ printk ("STP_EXIT received\n");
+ probe_exit();
+#ifndef STP_NETLINK_ONLY
+ relay_flush(app.chan);
+ ssleep(2); /* FIXME: time for data to be flushed */
+#endif
+ send_reply (STP_EXIT, __this_module.name, strlen(__this_module.name) + 1, stpd_pid);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * relay-app callbacks
*/
+static struct relay_app_callbacks stp_callbacks =
+{
+ .app_started = stpd_app_started,
+ .app_stopped = stpd_app_stopped,
+ .user_command = stpd_command
+};
-void _stp_print_str (char *str)
+int _stp_netlink_open(void)
+{
+ if (init_relay_app("stpd", "cpu", &stp_callbacks)) {
+ printk ("STP: couldn't init relay app\n");
+ return -1;
+ }
+ return 0;
+}
+
+void _stp_netlink_close (void)
{
- printk ("%s", str);
+ send_reply (STP_DONE, NULL, 0, stpd_pid);
+ close_relay_app();
}
/** @} */
diff --git a/runtime/map.c b/runtime/map.c
index 936383be..7e5caa49 100644
--- a/runtime/map.c
+++ b/runtime/map.c
@@ -8,6 +8,7 @@
#include "map.h"
#include "alloc.c"
+#include "string.c"
static int map_sizes[] = {
sizeof(struct map_node_int64),
@@ -641,6 +642,11 @@ void _stp_map_set_str(MAP map, char *val)
}
}
+void _stp_map_set_string (MAP map, String str)
+{
+ _stp_map_set_str (map, str->buf);
+}
+
/** Gets the current element's value.
* @param map
* @returns A string pointer. If the current element is not set or doesn't exist, returns NULL.
@@ -841,7 +847,7 @@ void _stp_list_clear(MAP map)
}
if (map->num != 0) {
- dlog ("ERROR: list is supposed to be empty (has %d)\n", map->num);
+ _stp_log ("ERROR: list is supposed to be empty (has %d)\n", map->num);
}
}
@@ -856,6 +862,12 @@ inline void _stp_list_add_str(MAP map, char *str)
_stp_map_set_str(map, str);
}
+inline void _stp_list_add_string (MAP map, String str)
+{
+ _stp_map_key_long(map, map->num);
+ _stp_map_set_str(map, str->buf);
+}
+
/** Adds an int64 to a list.
* @param map
* @param val
diff --git a/runtime/map.h b/runtime/map.h
index 9e30a926..65ed0674 100644
--- a/runtime/map.h
+++ b/runtime/map.h
@@ -150,12 +150,14 @@ typedef struct map_root *MAP;
* type of the argument.
* @note May cause compiler warning on some GCCs
*/
-#define _stp_map_set(map, val) \
+#define _stp_map_set(map, val) \
({ \
- if (__builtin_types_compatible_p (typeof (val), char[])) \
+ if (__builtin_types_compatible_p (typeof (val), char[])) \
_stp_map_set_str (map, (char *)(val)); \
- else \
- _stp_map_set_int64 (map, (int64_t)(val)); \
+ else if (__builtin_types_compatible_p (typeof (val), String)) \
+ _stp_map_set_string (map, (String)(val)); \
+ else \
+ _stp_map_set_int64 (map, (int64_t)(val)); \
})
/** Loop through all elements of a map or list.
@@ -178,11 +180,13 @@ typedef struct map_root *MAP;
*
* @note May cause compiler warning on some GCCs
*/
-#define _stp_list_add(map, val) \
- ({ \
- if (__builtin_types_compatible_p (typeof (val), char[])) \
+#define _stp_list_add(map, val) \
+ ({ \
+ if (__builtin_types_compatible_p (typeof (val), char[])) \
_stp_list_add_str (map, (char *)(val)); \
- else \
+ else if (__builtin_types_compatible_p (typeof (val), String)) \
+ _stp_list_add_string (map, (String)(val)); \
+ else \
_stp_list_add_int64 (map, (int64_t)(val)); \
})
diff --git a/runtime/print.c b/runtime/print.c
new file mode 100644
index 00000000..8b0d267d
--- /dev/null
+++ b/runtime/print.c
@@ -0,0 +1,168 @@
+#ifndef _PRINT_C_ /* -*- linux-c -*- */
+#define _PRINT_C_
+
+#include <linux/config.h>
+
+#include "io.c"
+
+/** @file print.c
+ * @addtogroup print Print Buffer
+ * Print Buffer 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.
+ * @{
+ */
+
+/** Size of buffer, not including terminating NULL */
+#define STP_PRINT_BUF_LEN 8000
+
+static int _stp_pbuf_len[NR_CPUS];
+
+#ifdef STP_NETLINK_ONLY
+#define STP_PRINT_BUF_START 0
+static char _stp_pbuf[NR_CPUS][STP_PRINT_BUF_LEN + 1];
+
+void _stp_print_flush (void)
+{
+ int cpu = smp_processor_id();
+ char *buf = &_stp_pbuf[cpu][0];
+ int len = _stp_pbuf_len[cpu];
+
+ if (len == 0)
+ return;
+
+ if ( app.logging == 0) {
+ _stp_pbuf_len[cpu] = 0;
+ return;
+ }
+
+ /* enforce newline at end */
+ if (buf[len - 1] != '\n') {
+ buf[len++] = '\n';
+ buf[len] = '\0';
+ }
+
+ send_reply (STP_REALTIME_DATA, buf, len + 1, stpd_pid);
+ _stp_pbuf_len[cpu] = 0;
+}
+
+#else /* ! STP_NETLINK_ONLY */
+/* size of timestamp, in bytes, including space */
+#define TIMESTAMP_SIZE 19
+#define STP_PRINT_BUF_START (TIMESTAMP_SIZE + 1)
+static char _stp_pbuf[NR_CPUS][STP_PRINT_BUF_LEN + STP_PRINT_BUF_START + 1];
+
+void _stp_print_flush (void)
+{
+ int cpu = smp_processor_id();
+ char *buf = &_stp_pbuf[cpu][0];
+ char *ptr = buf + STP_PRINT_BUF_START;
+ struct timeval tv;
+
+ if (_stp_pbuf_len[cpu] == 0)
+ return;
+
+ /* enforce newline at end */
+ if (ptr[_stp_pbuf_len[cpu]-1] != '\n') {
+ ptr[_stp_pbuf_len[cpu]++] = '\n';
+ ptr[_stp_pbuf_len[cpu]] = '\0';
+ }
+
+ do_gettimeofday(&tv);
+ scnprintf (buf, TIMESTAMP_SIZE+1, "[%li.%06li] ", tv.tv_sec, tv.tv_usec);
+ buf[TIMESTAMP_SIZE] = ' ';
+ relayapp_write(buf, _stp_pbuf_len[cpu] + TIMESTAMP_SIZE + 2);
+ _stp_pbuf_len[cpu] = 0;
+}
+#endif /* STP_NETLINK_ONLY */
+
+/** Sprint into the scratch buffer.
+ * Like printf, except output goes into a global scratch buffer
+ * which will contain the null-terminated output.
+ * Safe because overflowing the buffer is not allowed.
+ * Size is limited by length of scratch buffer, STP_BUF_LEN.
+ *
+ * @param fmt A printf-style format string followed by a
+ * variable number of args.
+ * @sa _stp_pbuf_clear
+ */
+
+void _stp_printf (const char *fmt, ...)
+{
+ int num;
+ va_list args;
+ int cpu = smp_processor_id();
+ char *buf = &_stp_pbuf[cpu][STP_PRINT_BUF_START] + _stp_pbuf_len[cpu];
+ va_start(args, fmt);
+ num = vscnprintf(buf, STP_PRINT_BUF_LEN - _stp_pbuf_len[cpu], fmt, args);
+ va_end(args);
+ if (num > 0)
+ _stp_pbuf_len[cpu] += num;
+}
+
+void _stp_vprintf (const char *fmt, va_list args)
+{
+ int num;
+ int cpu = smp_processor_id();
+ char *buf = &_stp_pbuf[cpu][STP_PRINT_BUF_START] + _stp_pbuf_len[cpu];
+ num = vscnprintf(buf, STP_PRINT_BUF_LEN -_stp_pbuf_len[cpu], fmt, args);
+ if (num > 0)
+ _stp_pbuf_len[cpu] += num;
+}
+
+/** Write a string into the scratch buffer.
+ * Copies a string into a global scratch buffer.
+ * Safe because overflowing the buffer is not allowed.
+ * Size is limited by length of scratch buffer, STP_BUF_LEN.
+ * This is more efficient than using _stp_sprint().
+ *
+ * @param str A string.
+ */
+
+void _stp_print_cstr (const char *str)
+{
+ int cpu = smp_processor_id();
+ char *buf = &_stp_pbuf[cpu][STP_PRINT_BUF_START] + _stp_pbuf_len[cpu];
+ int num = strlen (str);
+ if (num > STP_PRINT_BUF_LEN - _stp_pbuf_len[cpu])
+ num = STP_PRINT_BUF_LEN - _stp_pbuf_len[cpu];
+ strncpy (buf, str, num+1);
+ _stp_pbuf_len[cpu] += num;
+}
+
+/** Clear the scratch buffer.
+ * This function should be called before anything is written to
+ * the scratch buffer. Output will accumulate in the buffer
+ * until this function is called again.
+ * @returns A pointer to the buffer.
+ */
+
+char *_stp_print_clear (void)
+{
+ int cpu = smp_processor_id();
+ _stp_pbuf_len[cpu] = 0;
+ return &_stp_pbuf[cpu][STP_PRINT_BUF_START];
+}
+
+#include "string.c"
+
+void _stp_print_string (String str)
+{
+ if (str->len)
+ _stp_print_cstr (str->buf);
+}
+
+#define _stp_print(str) \
+ ({ \
+ if (__builtin_types_compatible_p (typeof (str), char[])) { \
+ char *x = (char *)str; \
+ _stp_print_cstr(x); \
+ } else { \
+ String x = (String)str; \
+ _stp_print_string(x); \
+ } \
+ })
+
+/** @} */
+#endif /* _PRINT_C_ */
diff --git a/runtime/probes.c b/runtime/probes.c
index 8b756251..62819901 100644
--- a/runtime/probes.c
+++ b/runtime/probes.c
@@ -20,7 +20,7 @@ void _stp_unregister_jprobes (struct jprobe *probes, int num_probes)
int i;
for (i = 0; i < num_probes; i++)
unregister_jprobe(&probes[i]);
- dlog ("All jprobes removed\n");
+ _stp_log ("All jprobes removed\n");
}
/** Register a group of jprobes.
@@ -37,12 +37,12 @@ int _stp_register_jprobes (struct jprobe *probes, int num_probes)
for (i = 0; i < num_probes; i++) {
addr =_stp_lookup_name((char *)probes[i].kp.addr);
if (addr == 0) {
- dlog ("ERROR: function %s not found!\n",
+ _stp_log ("ERROR: function %s not found!\n",
(char *)probes[i].kp.addr);
ret = -1; /* FIXME */
goto out;
}
- dlog("inserting jprobe at %s (%p)\n", probes[i].kp.addr, addr);
+ _stp_log("inserting jprobe at %s (%p)\n", probes[i].kp.addr, addr);
probes[i].kp.addr = (kprobe_opcode_t *)addr;
ret = register_jprobe(&probes[i]);
if (ret)
@@ -50,7 +50,7 @@ int _stp_register_jprobes (struct jprobe *probes, int num_probes)
}
return 0;
out:
- dlog ("probe module initialization failed. Exiting...\n");
+ _stp_log ("probe module initialization failed. Exiting...\n");
_stp_unregister_jprobes(probes, i);
return ret;
}
@@ -65,7 +65,7 @@ void _stp_unregister_kprobes (struct kprobe *probes, int num_probes)
int i;
for (i = 0; i < num_probes; i++)
unregister_kprobe(&probes[i]);
- dlog ("All kprobes removed\n");
+ _stp_log ("All kprobes removed\n");
}
/** Register a group of kprobes.
@@ -82,12 +82,12 @@ int _stp_register_kprobes (struct kprobe *probes, int num_probes)
for (i = 0; i < num_probes; i++) {
addr =_stp_lookup_name((char *)probes[i].addr);
if (addr == 0) {
- dlog ("ERROR: function %s not found!\n",
+ _stp_log ("ERROR: function %s not found!\n",
(char *)probes[i].addr);
ret = -1; /* FIXME */
goto out;
}
- dlog("inserting kprobe at %s (%p)\n", probes[i].addr, addr);
+ _stp_log("inserting kprobe at %s (%p)\n", probes[i].addr, addr);
probes[i].addr = (kprobe_opcode_t *)addr;
ret = register_kprobe(&probes[i]);
if (ret)
@@ -95,7 +95,7 @@ int _stp_register_kprobes (struct kprobe *probes, int num_probes)
}
return 0;
out:
- dlog ("probe module initialization failed. Exiting...\n");
+ _stp_log ("probe module initialization failed. Exiting...\n");
_stp_unregister_kprobes(probes, i);
return ret;
}
diff --git a/runtime/relay-app.h b/runtime/relay-app.h
new file mode 100644
index 00000000..220d82f9
--- /dev/null
+++ b/runtime/relay-app.h
@@ -0,0 +1,534 @@
+/*
+ * relay-app.h - kernel 'library' functions for typical relayfs applications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) IBM Corporation, 2005
+ *
+ * 2005-Feb Created by Tom Zanussi <zanussi@us.ibm.com>
+ *
+ * This header file encapsulates the details of channel setup and
+ * teardown and communication between the kernel and user parts of a
+ * typical and common type of relayfs application, which is that
+ * kernel logging is kicked off when a userspace data collection
+ * application starts and stopped when the collection app exits, and
+ * data is automatically logged to disk in-between. Channels are
+ * created when the collection app is started and destroyed when it
+ * exits, not when the kernel module is inserted, so different channel
+ * buffer sizes can be specified for each separate run via
+ * command-line options for instance.
+ *
+ * Writing to the channel is done using 2 macros, relayapp_write() and
+ * _relayapp_write(), which are just wrappers around relay_write() and
+ * _relay_write() but without the channel param. You can safely call
+ * these at any time - if there's no channel yet, they'll just be
+ * ignored.
+ *
+ * To create a relay-app application, do the following:
+ *
+ * In your kernel module:
+ *
+ * - #include "relay-app.h"
+ *
+ * - Call init_relay_app() in your module_init function, with the
+ * names of the directory to create relayfs files in and the base name
+ * of the per-cpu relayfs files e.g. to have /mnt/relay/myapp/cpuXXX
+ * created call init_relay_app("myapp", "cpu", callbacks).
+ *
+ * NOTE: The callbacks are entirely optional - pass NULL if you
+ * don't want to define any. If you want to define some but not
+ * others, just set the ones you want, and ignore or NULL out the
+ * others.
+ *
+ * NOTE: This won't actually create the relayfs files - that will
+ * happen when the userspace application starts (i.e. you can supply
+ * the buffer sizes on the application command-line for each new run
+ * of your program).
+ *
+ * NOTE: If you pass in NULL for the directory name, the relay files
+ * will be created in the root directory of the relayfs filesystem.
+ *
+ * - Call close_relay_app() in your module_exit function - this cleans
+ * up the control channel and the relay files from the previous run,
+ * if any.
+ *
+ * - relay-apps use a control channel to communicate initialization
+ * and status information between the kernel module and user space
+ * program. This is hidden beneath the API so you normally don't need
+ * to know anything about it, but if you want you can also use it to
+ * send user-defined commands from your user space application. To do
+ * this, you need to define a definition for the user_command()
+ * callback and in the callback sort out and handle handle the
+ * commands you send from user space (via send_request()). The
+ * callback must return 1 if the command was handled, or 0 if not
+ * (which will result in a send_error in the user space program,
+ * alerting you to the fact that you're sending something bogus).
+ *
+ * NOTE: Currently commands can only be sent before the user space
+ * application enters relay_app_main_loop() i.e. for initialization
+ * purposes only.
+ *
+ * - the app_started() and app_stopped() callbacks provide an
+ * opportunity for your kernel module to perform app-specific
+ * initialization and cleanup, if desired. They are purely
+ * informational. app_started() is called when the user space
+ * application has started and app_stopped() is called when the user
+ * space application has stopped.
+ *
+ * In your user space application do the following:
+ *
+ * - Call init_relay_app() with the names of the relayfs file base
+ * name and the base filename of the output files that will be
+ * created, as well as the sub-buffer size and count for the current
+ * run (which can be passed in on the command-line if you want). This
+ * will create the channel and set up the ouptut files and buffer
+ * mappings. e.g. to set up reading from the relayfs files specified in the
+ * above example and write them to a set of per-cpu output files named
+ * myoutputXXX:
+ *
+ * init_relay_app("/mnt/relay/myapp/cpu", "myoutput",
+ * subbuf_size_opt, n_subbufs_opt, 1);
+ *
+ * (the last parameter just specifies whether or not to print out a
+ * summary of the number of buffers processed, and the maximum backlog
+ * of sub-buffers encountered e.g. if you have 4 sub-buffers, a
+ * maximum backlog of 3 would mean that you came close to having a
+ * full buffer, so you might want to use more or bigger sub-buffers
+ * next time. Of course, if the buffers actually filled up, the
+ * maximum backlog would be 4 and you'd have lost data).
+ *
+ * - Call relay_app_main_loop(). This will set up an infinite loop
+ * (press Control-C to break out and finalize the data) which
+ * automatically reads the data from the relayfs buffers as it becomes
+ * available and and writes it out to per-cpu output files.
+ *
+ * NOTE: The control channel is implemented as a netlink socket.
+ * relay-app defaults to using NETLINK_USERSOCK for all
+ * applications, which means that you can't have more than 1
+ * relay-app in use at a time, unless you use different netlink
+ * 'units' for each one. If you want to have more than one
+ * relay-app in use at a time, you can specify a different netlink
+ * 'unit' by using the _init_relay_app() versions of the
+ * init_relay_app() functions, on both the kernel and user sides,
+ * which are the same as the init_relay_app() functions but add a
+ * netlink unit param. See netlink.h for the currently unused
+ * numbers.
+ */
+
+#include <linux/inet.h>
+#include <linux/ip.h>
+#include <linux/netlink.h>
+#include <linux/relayfs_fs.h>
+
+/* relay-app pseudo-API */
+
+/*
+ * relay-app callbacks
+ */
+struct relay_app_callbacks
+{
+ /*
+ * user_command - app-specific command callback
+ * @command: user-defined command id
+ * @data: user-defined data associated with the command
+ *
+ * Return value: 1 if this callback handled it, 0 if not
+ *
+ * define this callback to handle user-defined commands sent
+ * from the user space application via send_request()
+ *
+ * NOTE: user commands must be >= RELAY_APP_USERCMD_START
+ */
+ int (*user_command) (int command, void *data);
+
+ /*
+ * app_started - the user-space application has started
+ *
+ * Do app-specific initializations now, if desired
+ */
+ void (*app_started) (void);
+
+ /*
+ * app_stopped - the user-space application has stopped
+ *
+ * Do app-specific cleanup now, if desired
+ */
+ void (*app_stopped) (void);
+};
+
+/*
+ * relay-app API functions
+ */
+static int init_relay_app(const char *dirname,
+ const char *file_basename,
+ struct relay_app_callbacks *callbacks);
+static void close_relay_app(void);
+
+/*
+ * relay-app write wrapper macros - use these instead of directly
+ * using relay_write() and _relay_write() relayfs functions.
+ */
+#define relayapp_write(data, len) \
+ if (app.logging) relay_write(app.chan, data, len)
+
+#define _relayapp_write(data, len) \
+ if (app.logging) _relay_write(app.chan, data, len)
+
+/* relay-app control channel command values */
+enum
+{
+ RELAY_APP_BUF_INFO = 1,
+ RELAY_APP_SUBBUFS_CONSUMED,
+ RELAY_APP_START,
+ RELAY_APP_STOP,
+ RELAY_APP_CHAN_CREATE,
+ RELAY_APP_CHAN_DESTROY,
+ RELAY_APP_USERCMD_START = 32
+};
+
+/* SystemTap extensions */
+enum
+{
+ STP_REALTIME_DATA = RELAY_APP_USERCMD_START,
+ STP_EXIT,
+ STP_DONE
+};
+
+/* internal stuff below here */
+
+/* netlink control channel */
+static struct sock *control;
+static int seq;
+static int stpd_pid = 0;
+
+/* info for this application */
+static struct relay_app
+{
+ char dirname[1024];
+ char file_basename[1024];
+ struct relay_app_callbacks *cb;
+ struct rchan *chan;
+ struct dentry *dir;
+ int logging;
+ int mappings;
+} app;
+
+/*
+ * subbuf_start() relayfs callback.
+ */
+static int relay_app_subbuf_start(struct rchan_buf *buf,
+ void *subbuf,
+ unsigned prev_subbuf_idx,
+ void *prev_subbuf)
+{
+ unsigned padding = buf->padding[prev_subbuf_idx];
+ if (prev_subbuf)
+ *((unsigned *)prev_subbuf) = padding;
+
+ return sizeof(padding); /* reserve space for padding */
+}
+
+/*
+ * buf_full() relayfs callback.
+ */
+static void relay_app_buf_full(struct rchan_buf *buf,
+ unsigned subbuf_idx,
+ void *subbuf)
+{
+ unsigned padding = buf->padding[subbuf_idx];
+ *((unsigned *)subbuf) = padding;
+}
+
+static void relay_app_buf_mapped(struct rchan_buf *buf, struct file *filp)
+{
+ if (app.cb && app.cb->app_started && !app.mappings++)
+ app.cb->app_started();
+}
+
+static void relay_app_buf_unmapped(struct rchan_buf *buf, struct file *filp)
+{
+ if (app.cb && app.cb->app_started && !--app.mappings)
+ app.cb->app_stopped();
+}
+
+static struct rchan_callbacks app_rchan_callbacks =
+{
+ .subbuf_start = relay_app_subbuf_start,
+ .buf_full = relay_app_buf_full,
+ .buf_mapped = relay_app_buf_mapped,
+ .buf_unmapped = relay_app_buf_unmapped
+};
+
+/**
+ * create_app_chan - creates channel /mnt/relay/dirname/filebaseXXX
+ *
+ * Returns channel on success, NULL otherwise.
+ */
+static struct rchan *create_app_chan(unsigned subbuf_size,
+ unsigned n_subbufs)
+{
+ struct rchan *chan;
+
+ if (strlen(app.dirname)) {
+ app.dir = relayfs_create_dir(app.dirname, NULL);
+ if (!app.dir) {
+ printk("Couldn't create relayfs app directory %s.\n", app.dirname);
+ return NULL;
+ }
+ }
+
+ chan = relay_open(app.file_basename, app.dir, subbuf_size,
+ n_subbufs, 0, &app_rchan_callbacks);
+
+ if (!chan) {
+ printk("relay app channel creation failed\n");
+ if (app.dir)
+ relayfs_remove_dir(app.dir);
+ return NULL;
+ }
+
+ return chan;
+}
+
+/**
+ * destroy_app_chan - destroys channel /mnt/relay/dirname/filebaseXXX
+ */
+static void destroy_app_chan(struct rchan *chan)
+{
+ if (chan)
+ relay_close(chan);
+ if (app.dir)
+ relayfs_remove_dir(app.dir);
+
+ app.chan = NULL;
+ app.dir = NULL;
+}
+
+/* netlink control channel communication with userspace */
+
+struct buf_info
+{
+ int cpu;
+ unsigned produced;
+ unsigned consumed;
+};
+
+struct consumed_info
+{
+ int cpu;
+ unsigned consumed;
+};
+
+struct channel_create_info
+{
+ unsigned subbuf_size;
+ unsigned n_subbufs;
+};
+
+/*
+ * send_reply - send reply to userspace over netlink control channel
+ */
+static int send_reply(int type, void *reply, int len, int pid)
+{
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+ void *data;
+ int size;
+ int err;
+
+ size = NLMSG_SPACE(len);
+ skb = alloc_skb(size, GFP_ATOMIC);
+ if (!skb)
+ return -1;
+ nlh = NLMSG_PUT(skb, pid, seq++, type, size - sizeof(*nlh));
+ nlh->nlmsg_flags = 0;
+ data = NLMSG_DATA(nlh);
+ memcpy(data, reply, len);
+ err = netlink_unicast(control, skb, pid, MSG_DONTWAIT);
+
+ return 0;
+
+nlmsg_failure:
+ if (skb)
+ kfree_skb(skb);
+
+ return -1;
+}
+
+static void handle_buf_info(struct buf_info *in, int pid)
+{
+ struct buf_info out;
+
+ if (!app.chan)
+ return;
+
+ out.cpu = in->cpu;
+ out.produced = atomic_read(&app.chan->buf[in->cpu]->subbufs_produced);
+ out.consumed = atomic_read(&app.chan->buf[in->cpu]->subbufs_consumed);
+
+ send_reply(RELAY_APP_BUF_INFO, &out, sizeof(out), pid);
+}
+
+static inline void handle_subbufs_consumed(struct consumed_info *info)
+{
+ if (!app.chan)
+ return;
+
+ relay_subbufs_consumed(app.chan, info->cpu, info->consumed);
+}
+
+static inline void handle_create(struct channel_create_info *info)
+{
+ destroy_app_chan(app.chan);
+ app.chan = create_app_chan(info->subbuf_size, info->n_subbufs);
+ if(!app.chan)
+ return;
+ app.mappings = 0;
+}
+
+/*
+ * msg_rcv_skb - dispatch userspace requests from netlink control channel
+ */
+static void msg_rcv_skb(struct sk_buff *skb)
+{
+ struct nlmsghdr *nlh = NULL;
+ int pid, flags;
+ int nlmsglen, skblen;
+ void *data;
+
+ skblen = skb->len;
+
+ if (skblen < sizeof (*nlh))
+ return;
+
+ nlh = (struct nlmsghdr *)skb->data;
+ nlmsglen = nlh->nlmsg_len;
+
+ if (nlmsglen < sizeof(*nlh) || skblen < nlmsglen)
+ return;
+
+ stpd_pid = pid = nlh->nlmsg_pid;
+ flags = nlh->nlmsg_flags;
+
+ if (pid <= 0 || !(flags & NLM_F_REQUEST)) {
+ netlink_ack(skb, nlh, -EINVAL);
+ return;
+ }
+
+ if (flags & MSG_TRUNC) {
+ netlink_ack(skb, nlh, -ECOMM);
+ return;
+ }
+
+ data = NLMSG_DATA(nlh);
+
+ switch (nlh->nlmsg_type) {
+ case RELAY_APP_CHAN_CREATE:
+ handle_create(data);
+ break;
+ case RELAY_APP_CHAN_DESTROY:
+ destroy_app_chan(app.chan);
+ break;
+ case RELAY_APP_START:
+ app.logging = 1;
+ break;
+ case RELAY_APP_STOP:
+ app.logging = 0;
+ relay_flush(app.chan);
+ break;
+ case RELAY_APP_BUF_INFO:
+ handle_buf_info(data, pid);
+ break;
+ case RELAY_APP_SUBBUFS_CONSUMED:
+ handle_subbufs_consumed(data);
+ break;
+ default:
+ if (!app.cb || !app.cb->user_command ||
+ !app.cb->user_command(nlh->nlmsg_type, data))
+ netlink_ack(skb, nlh, -EINVAL);
+ return;
+ }
+
+ if (flags & NLM_F_ACK)
+ netlink_ack(skb, nlh, 0);
+}
+
+/*
+ * msg_rcv - handle netlink control channel requests
+ */
+static void msg_rcv(struct sock *sk, int len)
+{
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
+ msg_rcv_skb(skb);
+ kfree_skb(skb);
+ }
+}
+
+/*
+ * _init_relay_app - adds netlink 'unit' if other than NETLINK_USERSOCK wanted
+ */
+static int _init_relay_app(const char *dirname,
+ const char *file_basename,
+ struct relay_app_callbacks *callbacks,
+ int unit)
+{
+ if (!file_basename)
+ return -1;
+
+ if (dirname)
+ strncpy(app.dirname, dirname, 1024);
+ strncpy(app.file_basename, file_basename, 1024);
+ app.cb = callbacks;
+
+ control = netlink_kernel_create(unit, msg_rcv);
+ if (!control) {
+ printk("Couldn't create control channel\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * init_relay_app - initialize /mnt/relay/dirname/file_basenameXXX
+ * @dirname: the directory to contain relayfs files for this app
+ * @file_basename: the base filename of the relayfs files for this app
+ * @callbacks: the relay_app_callbacks implemented for this app
+ *
+ * Returns 0 on success, -1 otherwise.
+ *
+ * NOTE: this doesn't create the relayfs files. That happens via the
+ * control channel protocol.
+ */
+static int init_relay_app(const char *dirname,
+ const char *file_basename,
+ struct relay_app_callbacks *callbacks)
+{
+ return _init_relay_app(dirname, file_basename, callbacks, NETLINK_USERSOCK);
+}
+
+/**
+ * close_relay_app - close netlink socket and destroy channel if it exists
+ *
+ * Returns 0 on success, -1 otherwise.
+ */
+static void close_relay_app(void)
+{
+ if (control)
+ sock_release(control->sk_socket);
+ destroy_app_chan(app.chan);
+}
diff --git a/runtime/relayfs/Makefile b/runtime/relayfs/Makefile
new file mode 100644
index 00000000..220a4e32
--- /dev/null
+++ b/runtime/relayfs/Makefile
@@ -0,0 +1,12 @@
+#
+# relayfs Makefile
+#
+
+CFLAGS += -I $(RELAYFS_INCLUDE)
+
+obj-m += relayfs.o
+
+relayfs-y := relay.o inode.o buffers.o
+
+clean:
+ /bin/rm -rf *.o *.ko *~ *.mod.c .*.cmd .tmp_versions
diff --git a/runtime/relayfs/buffers.c b/runtime/relayfs/buffers.c
new file mode 100644
index 00000000..476eafd8
--- /dev/null
+++ b/runtime/relayfs/buffers.c
@@ -0,0 +1,195 @@
+/*
+ * RelayFS buffer management code.
+ *
+ * Copyright (C) 2002-2005 - Tom Zanussi (zanussi@us.ibm.com), IBM Corp
+ * Copyright (C) 1999-2005 - Karim Yaghmour (karim@opersys.com)
+ *
+ * This file is released under the GPL.
+ */
+
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/relayfs_fs.h>
+#include "relay.h"
+#include "buffers.h"
+
+/*
+ * close() vm_op implementation for relayfs file mapping.
+ */
+static void relay_file_mmap_close(struct vm_area_struct *vma)
+{
+ struct rchan_buf *buf = vma->vm_private_data;
+ buf->chan->cb->buf_unmapped(buf, vma->vm_file);
+}
+
+/*
+ * nopage() vm_op implementation for relayfs file mapping.
+ */
+static struct page *relay_buf_nopage(struct vm_area_struct *vma,
+ unsigned long address,
+ int *type)
+{
+ struct page *page;
+ struct rchan_buf *buf = vma->vm_private_data;
+ unsigned long offset = address - vma->vm_start;
+
+ if (address > vma->vm_end)
+ return NOPAGE_SIGBUS; /* Disallow mremap */
+ if (!buf)
+ return NOPAGE_OOM;
+
+ page = vmalloc_to_page(buf->start + offset);
+ if (!page)
+ return NOPAGE_OOM;
+ get_page(page);
+
+ if (type)
+ *type = VM_FAULT_MINOR;
+
+ return page;
+}
+
+/*
+ * vm_ops for relay file mappings.
+ */
+static struct vm_operations_struct relay_file_mmap_ops = {
+ .nopage = relay_buf_nopage,
+ .close = relay_file_mmap_close,
+};
+
+/**
+ * relay_mmap_buf: - mmap channel buffer to process address space
+ * @buf: relay channel buffer
+ * @vma: vm_area_struct describing memory to be mapped
+ *
+ * Returns 0 if ok, negative on error
+ *
+ * Caller should already have grabbed mmap_sem.
+ */
+int relay_mmap_buf(struct rchan_buf *buf, struct vm_area_struct *vma)
+{
+ unsigned long length = vma->vm_end - vma->vm_start;
+ struct file *filp = vma->vm_file;
+
+ if (!buf)
+ return -EBADF;
+
+ if (length != (unsigned long)buf->chan->alloc_size)
+ return -EINVAL;
+
+ vma->vm_ops = &relay_file_mmap_ops;
+ vma->vm_private_data = buf;
+ buf->chan->cb->buf_mapped(buf, filp);
+
+ return 0;
+}
+
+/**
+ * relay_alloc_buf - allocate a channel buffer
+ * @buf: the buffer struct
+ * @size: total size of the buffer
+ *
+ * Returns a pointer to the resulting buffer, NULL if unsuccessful
+ */
+static void *relay_alloc_buf(struct rchan_buf *buf, unsigned long size)
+{
+ void *mem;
+ int i, j, n_pages;
+
+ size = PAGE_ALIGN(size);
+ n_pages = size >> PAGE_SHIFT;
+
+ buf->page_array = kcalloc(n_pages, sizeof(struct page *), GFP_KERNEL);
+ if (!buf->page_array)
+ return NULL;
+
+ for (i = 0; i < n_pages; i++) {
+ buf->page_array[i] = alloc_page(GFP_KERNEL);
+ if (unlikely(!buf->page_array[i]))
+ goto depopulate;
+ }
+ mem = vmap(buf->page_array, n_pages, GFP_KERNEL, PAGE_KERNEL);
+ if (!mem)
+ goto depopulate;
+
+ memset(mem, 0, size);
+ buf->page_count = n_pages;
+ return mem;
+
+depopulate:
+ for (j = 0; j < i; j++)
+ __free_page(buf->page_array[j]);
+ kfree(buf->page_array);
+ return NULL;
+}
+
+/**
+ * relay_create_buf - allocate and initialize a channel buffer
+ * @alloc_size: size of the buffer to allocate
+ * @n_subbufs: number of sub-buffers in the channel
+ *
+ * Returns channel buffer if successful, NULL otherwise
+ */
+struct rchan_buf *relay_create_buf(struct rchan *chan)
+{
+ struct rchan_buf *buf = kcalloc(1, sizeof(struct rchan_buf), GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->padding = kmalloc(chan->n_subbufs * sizeof(unsigned *), GFP_KERNEL);
+ if (!buf->padding)
+ goto free_buf;
+
+ buf->commit = kmalloc(chan->n_subbufs * sizeof(unsigned *), GFP_KERNEL);
+ if (!buf->commit)
+ goto free_buf;
+
+ buf->start = relay_alloc_buf(buf, chan->alloc_size);
+ if (!buf->start)
+ goto free_buf;
+
+ buf->chan = chan;
+ kref_get(&buf->chan->kref);
+ return buf;
+
+free_buf:
+ kfree(buf->commit);
+ kfree(buf->padding);
+ kfree(buf);
+ return NULL;
+}
+
+/**
+ * relay_destroy_buf - destroy an rchan_buf struct and associated buffer
+ * @buf: the buffer struct
+ */
+void relay_destroy_buf(struct rchan_buf *buf)
+{
+ struct rchan *chan = buf->chan;
+ int i;
+
+ if (likely(buf->start)) {
+ vunmap(buf->start);
+ for (i = 0; i < buf->page_count; i++)
+ __free_page(buf->page_array[i]);
+ kfree(buf->page_array);
+ }
+ kfree(buf->padding);
+ kfree(buf->commit);
+ kfree(buf);
+ kref_put(&chan->kref, relay_destroy_channel);
+}
+
+/**
+ * relay_remove_buf - remove a channel buffer
+ *
+ * Removes the file from the relayfs fileystem, which also frees the
+ * rchan_buf_struct and the channel buffer. Should only be called from
+ * kref_put().
+ */
+void relay_remove_buf(struct kref *kref)
+{
+ struct rchan_buf *buf = container_of(kref, struct rchan_buf, kref);
+ relayfs_remove(buf->dentry);
+}
diff --git a/runtime/relayfs/buffers.h b/runtime/relayfs/buffers.h
new file mode 100644
index 00000000..37a12493
--- /dev/null
+++ b/runtime/relayfs/buffers.h
@@ -0,0 +1,12 @@
+#ifndef _BUFFERS_H
+#define _BUFFERS_H
+
+/* This inspired by rtai/shmem */
+#define FIX_SIZE(x) (((x) - 1) & PAGE_MASK) + PAGE_SIZE
+
+extern int relay_mmap_buf(struct rchan_buf *buf, struct vm_area_struct *vma);
+extern struct rchan_buf *relay_create_buf(struct rchan *chan);
+extern void relay_destroy_buf(struct rchan_buf *buf);
+extern void relay_remove_buf(struct kref *kref);
+
+#endif/* _BUFFERS_H */
diff --git a/runtime/relayfs/build b/runtime/relayfs/build
new file mode 100755
index 00000000..a0eece3a
--- /dev/null
+++ b/runtime/relayfs/build
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+make V=1 -C /lib/modules/`uname -r`/build M=`pwd` modules \
+ RELAYFS_INCLUDE=`pwd`
diff --git a/runtime/relayfs/inode.c b/runtime/relayfs/inode.c
new file mode 100644
index 00000000..da32b7d2
--- /dev/null
+++ b/runtime/relayfs/inode.c
@@ -0,0 +1,423 @@
+/*
+ * VFS-related code for RelayFS, a high-speed data relay filesystem.
+ *
+ * Copyright (C) 2003-2005 - Tom Zanussi <zanussi@us.ibm.com>, IBM Corp
+ * Copyright (C) 2003-2005 - Karim Yaghmour <karim@opersys.com>
+ *
+ * Based on ramfs, Copyright (C) 2002 - Linus Torvalds
+ *
+ * This file is released under the GPL.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/pagemap.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/backing-dev.h>
+#include <linux/namei.h>
+#include <linux/poll.h>
+#include <linux/relayfs_fs.h>
+#include "relay.h"
+#include "buffers.h"
+
+#define RELAYFS_MAGIC 0xF0B4A981
+
+static struct vfsmount * relayfs_mount;
+static int relayfs_mount_count;
+static kmem_cache_t * relayfs_inode_cachep;
+
+static struct backing_dev_info relayfs_backing_dev_info = {
+ .ra_pages = 0, /* No readahead */
+ .memory_backed = 1, /* Does not contribute to dirty memory */
+};
+
+static struct inode *relayfs_get_inode(struct super_block *sb, int mode,
+ struct rchan *chan)
+{
+ struct rchan_buf *buf = NULL;
+ struct inode *inode;
+
+ if (S_ISREG(mode)) {
+ BUG_ON(!chan);
+ buf = relay_create_buf(chan);
+ if (!buf)
+ return NULL;
+ }
+
+ inode = new_inode(sb);
+ if (!inode) {
+ relay_destroy_buf(buf);
+ return NULL;
+ }
+
+ inode->i_mode = mode;
+ inode->i_uid = 0;
+ inode->i_gid = 0;
+ inode->i_blksize = PAGE_CACHE_SIZE;
+ inode->i_blocks = 0;
+ inode->i_mapping->backing_dev_info = &relayfs_backing_dev_info;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ switch (mode & S_IFMT) {
+ case S_IFREG:
+ inode->i_fop = &relayfs_file_operations;
+ RELAYFS_I(inode)->buf = buf;
+ break;
+ case S_IFDIR:
+ inode->i_op = &simple_dir_inode_operations;
+ inode->i_fop = &simple_dir_operations;
+
+ /* directory inodes start off with i_nlink == 2 (for "." entry) */
+ inode->i_nlink++;
+ break;
+ default:
+ break;
+ }
+
+ return inode;
+}
+
+/**
+ * relayfs_create_entry - create a relayfs directory or file
+ * @name: the name of the file to create
+ * @parent: parent directory
+ * @mode: mode
+ * @chan: relay channel associated with the file
+ *
+ * Returns the new dentry, NULL on failure
+ *
+ * Creates a file or directory with the specifed permissions.
+ */
+static struct dentry *relayfs_create_entry(const char *name,
+ struct dentry *parent,
+ int mode,
+ struct rchan *chan)
+{
+ struct qstr qname;
+ struct dentry *d;
+ struct inode *inode;
+ int error = 0;
+
+ BUG_ON(!name || !(S_ISREG(mode) || S_ISDIR(mode)));
+
+ error = simple_pin_fs("relayfs", &relayfs_mount, &relayfs_mount_count);
+ if (error) {
+ printk(KERN_ERR "Couldn't mount relayfs: errcode %d\n", error);
+ return NULL;
+ }
+
+ qname.name = name;
+ qname.len = strlen(name);
+ qname.hash = full_name_hash(name, qname.len);
+
+ if (!parent && relayfs_mount && relayfs_mount->mnt_sb)
+ parent = relayfs_mount->mnt_sb->s_root;
+
+ if (!parent) {
+ simple_release_fs(&relayfs_mount, &relayfs_mount_count);
+ return NULL;
+ }
+
+ parent = dget(parent);
+ down(&parent->d_inode->i_sem);
+ d = lookup_hash(&qname, parent);
+ if (IS_ERR(d)) {
+ d = NULL;
+ goto release_mount;
+ }
+
+ if (d->d_inode) {
+ d = NULL;
+ goto release_mount;
+ }
+
+ inode = relayfs_get_inode(parent->d_inode->i_sb, mode, chan);
+ if (!inode) {
+ d = NULL;
+ goto release_mount;
+ }
+
+ d_instantiate(d, inode);
+ dget(d); /* Extra count - pin the dentry in core */
+
+ if (S_ISDIR(mode))
+ parent->d_inode->i_nlink++;
+
+ goto exit;
+
+release_mount:
+ simple_release_fs(&relayfs_mount, &relayfs_mount_count);
+
+exit:
+ up(&parent->d_inode->i_sem);
+ dput(parent);
+ return d;
+}
+
+/**
+ * relayfs_create_file - create a file in the relay filesystem
+ * @name: the name of the file to create
+ * @parent: parent directory
+ * @mode: mode, if not specied the default perms are used
+ * @chan: channel associated with the file
+ *
+ * Returns file dentry if successful, NULL otherwise.
+ *
+ * The file will be created user r on behalf of current user.
+ */
+struct dentry *relayfs_create_file(const char *name, struct dentry *parent,
+ int mode, struct rchan *chan)
+{
+ if (!mode)
+ mode = S_IRUSR;
+ mode = (mode & S_IALLUGO) | S_IFREG;
+
+ return relayfs_create_entry(name, parent, mode, chan);
+}
+
+/**
+ * relayfs_create_dir - create a directory in the relay filesystem
+ * @name: the name of the directory to create
+ * @parent: parent directory, NULL if parent should be fs root
+ *
+ * Returns directory dentry if successful, NULL otherwise.
+ *
+ * The directory will be created world rwx on behalf of current user.
+ */
+struct dentry *relayfs_create_dir(const char *name, struct dentry *parent)
+{
+ int mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
+ return relayfs_create_entry(name, parent, mode, NULL);
+}
+
+/**
+ * relayfs_remove - remove a file or directory in the relay filesystem
+ * @dentry: file or directory dentry
+ */
+int relayfs_remove(struct dentry *dentry)
+{
+ struct dentry *parent = dentry->d_parent;
+ if (!parent)
+ return -EINVAL;
+
+ parent = dget(parent);
+ down(&parent->d_inode->i_sem);
+ if (dentry->d_inode) {
+ simple_unlink(parent->d_inode, dentry);
+ d_delete(dentry);
+ }
+ dput(dentry);
+ up(&parent->d_inode->i_sem);
+ dput(parent);
+
+ simple_release_fs(&relayfs_mount, &relayfs_mount_count);
+
+ return 0;
+}
+
+/**
+ * relayfs_remove_dir - remove a directory in the relay filesystem
+ * @dentry: directory dentry
+ *
+ * Returns 0 if successful, negative otherwise.
+ */
+int relayfs_remove_dir(struct dentry *dentry)
+{
+ if (!dentry)
+ return -EINVAL;
+
+ return relayfs_remove(dentry);
+}
+
+/**
+ * relayfs_open - open file op for relayfs files
+ * @inode: the inode
+ * @filp: the file
+ *
+ * Increments the channel buffer refcount.
+ */
+int relayfs_open(struct inode *inode, struct file *filp)
+{
+ struct rchan_buf *buf = RELAYFS_I(inode)->buf;
+ kref_get(&buf->kref);
+
+ return 0;
+}
+
+/**
+ * relayfs_mmap - mmap file op for relayfs files
+ * @filp: the file
+ * @vma: the vma describing what to map
+ *
+ * Calls upon relay_mmap_buf to map the file into user space.
+ */
+int relayfs_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ return relay_mmap_buf(RELAYFS_I(inode)->buf, vma);
+}
+
+/**
+ * relayfs_poll - poll file op for relayfs files
+ * @filp: the file
+ * @wait: poll table
+ *
+ * Poll implemention.
+ */
+unsigned int relayfs_poll(struct file *filp, poll_table *wait)
+{
+ unsigned int mask = 0;
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct rchan_buf *buf = RELAYFS_I(inode)->buf;
+
+ if (buf->finalized)
+ return POLLERR;
+
+ if (filp->f_mode & FMODE_READ) {
+ poll_wait(filp, &buf->read_wait, wait);
+ if (!relay_buf_empty(buf))
+ mask |= POLLIN | POLLRDNORM;
+ }
+
+ return mask;
+}
+
+/**
+ * relayfs_release - release file op for relayfs files
+ * @inode: the inode
+ * @filp: the file
+ *
+ * Decrements the channel refcount, as the filesystem is
+ * no longer using it.
+ */
+int relayfs_release(struct inode *inode, struct file *filp)
+{
+ struct rchan_buf *buf = RELAYFS_I(inode)->buf;
+ kref_put(&buf->kref, relay_remove_buf);
+
+ return 0;
+}
+
+/**
+ * relayfs alloc_inode() implementation
+ */
+static struct inode *relayfs_alloc_inode(struct super_block *sb)
+{
+ struct relayfs_inode_info *p = kmem_cache_alloc(relayfs_inode_cachep, SLAB_KERNEL);
+ if (!p)
+ return NULL;
+ p->buf = NULL;
+
+ return &p->vfs_inode;
+}
+
+/**
+ * relayfs destroy_inode() implementation
+ */
+static void relayfs_destroy_inode(struct inode *inode)
+{
+ if (RELAYFS_I(inode)->buf)
+ relay_destroy_buf(RELAYFS_I(inode)->buf);
+
+ kmem_cache_free(relayfs_inode_cachep, RELAYFS_I(inode));
+}
+
+static void init_once(void *p, kmem_cache_t *cachep, unsigned long flags)
+{
+ struct relayfs_inode_info *i = p;
+ if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR)
+ inode_init_once(&i->vfs_inode);
+}
+
+struct file_operations relayfs_file_operations = {
+ .open = relayfs_open,
+ .poll = relayfs_poll,
+ .mmap = relayfs_mmap,
+ .release = relayfs_release,
+};
+
+static struct super_operations relayfs_ops = {
+ .statfs = simple_statfs,
+ .drop_inode = generic_delete_inode,
+ .alloc_inode = relayfs_alloc_inode,
+ .destroy_inode = relayfs_destroy_inode,
+};
+
+static int relayfs_fill_super(struct super_block * sb, void * data, int silent)
+{
+ struct inode *inode;
+ struct dentry *root;
+ int mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
+
+ sb->s_blocksize = PAGE_CACHE_SIZE;
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ sb->s_magic = RELAYFS_MAGIC;
+ sb->s_op = &relayfs_ops;
+ inode = relayfs_get_inode(sb, mode, NULL);
+
+ if (!inode)
+ return -ENOMEM;
+
+ root = d_alloc_root(inode);
+ if (!root) {
+ iput(inode);
+ return -ENOMEM;
+ }
+ sb->s_root = root;
+
+ return 0;
+}
+
+static struct super_block * relayfs_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name,
+ void *data)
+{
+ return get_sb_single(fs_type, flags, data, relayfs_fill_super);
+}
+
+static struct file_system_type relayfs_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "relayfs",
+ .get_sb = relayfs_get_sb,
+ .kill_sb = kill_litter_super,
+};
+
+static int __init init_relayfs_fs(void)
+{
+ int err;
+
+ relayfs_inode_cachep = kmem_cache_create("relayfs_inode_cache",
+ sizeof(struct relayfs_inode_info), 0,
+ 0, init_once, NULL);
+ if (!relayfs_inode_cachep)
+ return -ENOMEM;
+
+ err = register_filesystem(&relayfs_fs_type);
+ if (err)
+ kmem_cache_destroy(relayfs_inode_cachep);
+
+ return err;
+}
+
+static void __exit exit_relayfs_fs(void)
+{
+ unregister_filesystem(&relayfs_fs_type);
+ kmem_cache_destroy(relayfs_inode_cachep);
+}
+
+module_init(init_relayfs_fs)
+module_exit(exit_relayfs_fs)
+
+EXPORT_SYMBOL_GPL(relayfs_open);
+EXPORT_SYMBOL_GPL(relayfs_poll);
+EXPORT_SYMBOL_GPL(relayfs_mmap);
+EXPORT_SYMBOL_GPL(relayfs_release);
+EXPORT_SYMBOL_GPL(relayfs_file_operations);
+EXPORT_SYMBOL_GPL(relayfs_create_dir);
+EXPORT_SYMBOL_GPL(relayfs_remove_dir);
+
+MODULE_AUTHOR("Tom Zanussi <zanussi@us.ibm.com> and Karim Yaghmour <karim@opersys.com>");
+MODULE_DESCRIPTION("Relay Filesystem");
+MODULE_LICENSE("GPL");
+
diff --git a/runtime/relayfs/linux/relayfs_fs.h b/runtime/relayfs/linux/relayfs_fs.h
new file mode 100644
index 00000000..1f697c3b
--- /dev/null
+++ b/runtime/relayfs/linux/relayfs_fs.h
@@ -0,0 +1,263 @@
+/*
+ * linux/include/linux/relayfs_fs.h
+ *
+ * Copyright (C) 2002, 2003 - Tom Zanussi (zanussi@us.ibm.com), IBM Corp
+ * Copyright (C) 1999, 2000, 2001, 2002 - Karim Yaghmour (karim@opersys.com)
+ *
+ * RelayFS definitions and declarations
+ */
+
+#ifndef _LINUX_RELAYFS_FS_H
+#define _LINUX_RELAYFS_FS_H
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/kref.h>
+
+/*
+ * Tracks changes to rchan_buf struct
+ */
+#define RELAYFS_CHANNEL_VERSION 3
+
+/*
+ * Per-cpu relay channel buffer
+ */
+struct rchan_buf
+{
+ void *start; /* start of channel buffer */
+ void *data; /* start of current sub-buffer */
+ unsigned offset; /* current offset into sub-buffer */
+ atomic_t subbufs_produced; /* count of sub-buffers produced */
+ atomic_t subbufs_consumed; /* count of sub-buffers consumed */
+ atomic_t unfull; /* state has gone from full to not */
+ struct rchan *chan; /* associated channel */
+ wait_queue_head_t read_wait; /* reader wait queue */
+ struct work_struct wake_readers; /* reader wake-up work struct */
+ struct dentry *dentry; /* channel file dentry */
+ struct kref kref; /* channel buffer refcount */
+ struct page **page_array; /* array of current buffer pages */
+ int page_count; /* number of current buffer pages */
+ unsigned *padding; /* padding counts per sub-buffer */
+ unsigned *commit; /* commit counts per sub-buffer */
+ int finalized; /* buffer has been finalized */
+} ____cacheline_aligned;
+
+/*
+ * Relay channel data structure
+ */
+struct rchan
+{
+ u32 version; /* the version of this struct */
+ unsigned subbuf_size; /* sub-buffer size */
+ unsigned n_subbufs; /* number of sub-buffers per buffer */
+ unsigned alloc_size; /* total buffer size allocated */
+ int overwrite; /* overwrite buffer when full? */
+ struct rchan_callbacks *cb; /* client callbacks */
+ struct kref kref; /* channel refcount */
+ struct rchan_buf *buf[NR_CPUS]; /* per-cpu channel buffers */
+};
+
+/*
+ * Relayfs inode
+ */
+struct relayfs_inode_info
+{
+ struct inode vfs_inode;
+ struct rchan_buf *buf;
+};
+
+static inline struct relayfs_inode_info *RELAYFS_I(struct inode *inode)
+{
+ return container_of(inode, struct relayfs_inode_info, vfs_inode);
+}
+
+/*
+ * Relay channel client callbacks
+ */
+struct rchan_callbacks
+{
+ /*
+ * subbuf_start - called on buffer-switch to a new sub-buffer
+ * @buf: the channel buffer containing the new sub-buffer
+ * @subbuf: the start of the new sub-buffer
+ * @prev_subbuf_idx: the previous sub-buffer's index
+ * @prev_subbuf: the start of the previous sub-buffer
+ *
+ * NOTE: subbuf_start will also be invoked when the buffer is
+ * created, so that the first sub-buffer can be initialized
+ * if necessary. In this case, prev_subbuf will be NULL.
+ */
+ int (*subbuf_start) (struct rchan_buf *buf,
+ void *subbuf,
+ unsigned prev_subbuf_idx,
+ void *prev_subbuf);
+
+ /*
+ * deliver - deliver a guaranteed full sub-buffer to client
+ * @buf: the channel buffer containing the sub-buffer
+ * @subbuf_idx: the sub-buffer's index
+ * @subbuf: the start of the new sub-buffer
+ *
+ * Only works if relay_commit is also used
+ */
+ void (*deliver) (struct rchan_buf *buf,
+ unsigned subbuf_idx,
+ void *subbuf);
+
+ /*
+ * buf_mapped - relayfs buffer mmap notification
+ * @buf: the channel buffer
+ * @filp: relayfs file pointer
+ *
+ * Called when a relayfs file is successfully mmapped
+ */
+ void (*buf_mapped)(struct rchan_buf *buf,
+ struct file *filp);
+
+ /*
+ * buf_unmapped - relayfs buffer unmap notification
+ * @buf: the channel buffer
+ * @filp: relayfs file pointer
+ *
+ * Called when a relayfs file is successfully unmapped
+ */
+ void (*buf_unmapped)(struct rchan_buf *buf,
+ struct file *filp);
+
+ /*
+ * buf_full - relayfs buffer full notification
+ * @buf: the channel channel buffer
+ * @subbuf_idx: the current sub-buffer's index
+ * @subbuf: the start of the current sub-buffer
+ *
+ * Called when a relayfs buffer becomes full
+ */
+ void (*buf_full)(struct rchan_buf *buf,
+ unsigned subbuf_idx,
+ void *subbuf);
+};
+
+/*
+ * relayfs kernel API, fs/relayfs/relay.c
+ */
+
+struct rchan *relay_open(const char *base_filename,
+ struct dentry *parent,
+ unsigned subbuf_size,
+ unsigned n_subbufs,
+ int overwrite,
+ struct rchan_callbacks *cb);
+extern void relay_close(struct rchan *chan);
+extern void relay_flush(struct rchan *chan);
+extern void relay_subbufs_consumed(struct rchan *chan,
+ int cpu,
+ int subbufs_consumed);
+extern void relay_reset(struct rchan *chan);
+extern unsigned relay_switch_subbuf(struct rchan_buf *buf,
+ unsigned length);
+extern void relay_commit(struct rchan_buf *buf,
+ void *reserved,
+ unsigned count);
+extern struct dentry *relayfs_create_dir(const char *name,
+ struct dentry *parent);
+extern int relayfs_remove_dir(struct dentry *dentry);
+
+/**
+ * relay_write - write data into the channel
+ * @chan: relay channel
+ * @data: data to be written
+ * @length: number of bytes to write
+ *
+ * Writes data into the current cpu's channel buffer.
+ *
+ * Protects the buffer by disabling interrupts. Use this
+ * if you might be logging from interrupt context. Try
+ * __relay_write() if you know you won't be logging from
+ * interrupt context.
+ */
+static inline void relay_write(struct rchan *chan,
+ const void *data,
+ unsigned length)
+{
+ unsigned long flags;
+ struct rchan_buf *buf;
+
+ local_irq_save(flags);
+ buf = chan->buf[smp_processor_id()];
+ if (unlikely(buf->offset + length > chan->subbuf_size))
+ length = relay_switch_subbuf(buf, length);
+ memcpy(buf->data + buf->offset, data, length);
+ buf->offset += length;
+ local_irq_restore(flags);
+}
+
+/**
+ * __relay_write - write data into the channel
+ * @chan: relay channel
+ * @data: data to be written
+ * @length: number of bytes to write
+ *
+ * Writes data into the current cpu's channel buffer.
+ *
+ * Protects the buffer by disabling preemption. Use
+ * relay_write() if you might be logging from interrupt
+ * context.
+ */
+static inline void __relay_write(struct rchan *chan,
+ const void *data,
+ unsigned length)
+{
+ struct rchan_buf *buf;
+
+ buf = chan->buf[get_cpu()];
+ if (unlikely(buf->offset + length > buf->chan->subbuf_size))
+ length = relay_switch_subbuf(buf, length);
+ memcpy(buf->data + buf->offset, data, length);
+ buf->offset += length;
+ put_cpu();
+}
+
+/**
+ * relay_reserve - reserve slot in channel buffer
+ * @chan: relay channel
+ * @length: number of bytes to reserve
+ *
+ * Returns pointer to reserved slot, NULL if full.
+ *
+ * Reserves a slot in the current cpu's channel buffer.
+ * Does not protect the buffer at all - caller must provide
+ * appropriate synchronization.
+ */
+static inline void *relay_reserve(struct rchan *chan, unsigned length)
+{
+ void *reserved;
+ struct rchan_buf *buf = chan->buf[smp_processor_id()];
+
+ if (unlikely(buf->offset + length > buf->chan->subbuf_size)) {
+ length = relay_switch_subbuf(buf, length);
+ if (!length)
+ return NULL;
+ }
+ reserved = buf->data + buf->offset;
+ buf->offset += length;
+
+ return reserved;
+}
+
+/*
+ * exported relayfs file operations, fs/relayfs/inode.c
+ */
+
+extern struct file_operations relayfs_file_operations;
+extern int relayfs_open(struct inode *inode, struct file *filp);
+extern unsigned int relayfs_poll(struct file *filp, poll_table *wait);
+extern int relayfs_mmap(struct file *filp, struct vm_area_struct *vma);
+extern int relayfs_release(struct inode *inode, struct file *filp);
+
+#endif /* _LINUX_RELAYFS_FS_H */
+
diff --git a/runtime/relayfs/relay.c b/runtime/relayfs/relay.c
new file mode 100644
index 00000000..82ad71c7
--- /dev/null
+++ b/runtime/relayfs/relay.c
@@ -0,0 +1,530 @@
+/*
+ * Public API and common code for RelayFS.
+ *
+ * See Documentation/filesystems/relayfs.txt for an overview of relayfs.
+ *
+ * Copyright (C) 2002-2005 - Tom Zanussi (zanussi@us.ibm.com), IBM Corp
+ * Copyright (C) 1999-2005 - Karim Yaghmour (karim@opersys.com)
+ *
+ * This file is released under the GPL.
+ */
+
+#include <linux/errno.h>
+#include <linux/stddef.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/relayfs_fs.h>
+#include "relay.h"
+#include "buffers.h"
+
+/**
+ * relay_buf_empty - boolean, is the channel buffer empty?
+ * @buf: channel buffer
+ *
+ * Returns 1 if the buffer is empty, 0 otherwise.
+ */
+int relay_buf_empty(struct rchan_buf *buf)
+{
+ int produced = atomic_read(&buf->subbufs_produced);
+ int consumed = atomic_read(&buf->subbufs_consumed);
+
+ return (produced - consumed) ? 0 : 1;
+}
+
+/**
+ * relay_buf_full - boolean, is the channel buffer full?
+ * @buf: channel buffer
+ *
+ * Returns 1 if the buffer is full, 0 otherwise.
+ */
+static inline int relay_buf_full(struct rchan_buf *buf)
+{
+ int produced, consumed;
+
+ if (buf->chan->overwrite)
+ return 0;
+
+ produced = atomic_read(&buf->subbufs_produced);
+ consumed = atomic_read(&buf->subbufs_consumed);
+
+ return (produced - consumed > buf->chan->n_subbufs - 1) ? 1 : 0;
+}
+
+/*
+ * High-level relayfs kernel API and associated functions.
+ */
+
+/*
+ * rchan_callback implementations defining default channel behavior. Used
+ * in place of corresponding NULL values in client callback struct.
+ */
+
+/*
+ * subbuf_start() default callback. Does nothing.
+ */
+static int subbuf_start_default_callback (struct rchan_buf *buf,
+ void *subbuf,
+ unsigned prev_subbuf_idx,
+ void *prev_subbuf)
+{
+ return 0;
+}
+
+/*
+ * deliver() default callback. Does nothing.
+ */
+static void deliver_default_callback (struct rchan_buf *buf,
+ unsigned subbuf_idx,
+ void *subbuf)
+{
+}
+
+/*
+ * buf_mapped() default callback. Does nothing.
+ */
+static void buf_mapped_default_callback(struct rchan_buf *buf,
+ struct file *filp)
+{
+}
+
+/*
+ * buf_unmapped() default callback. Does nothing.
+ */
+static void buf_unmapped_default_callback(struct rchan_buf *buf,
+ struct file *filp)
+{
+}
+
+/*
+ * buf_full() default callback. Does nothing.
+ */
+static void buf_full_default_callback(struct rchan_buf *buf,
+ unsigned subbuf_idx,
+ void *subbuf)
+{
+}
+
+/* relay channel default callbacks */
+static struct rchan_callbacks default_channel_callbacks = {
+ .subbuf_start = subbuf_start_default_callback,
+ .deliver = deliver_default_callback,
+ .buf_mapped = buf_mapped_default_callback,
+ .buf_unmapped = buf_unmapped_default_callback,
+ .buf_full = buf_full_default_callback,
+};
+
+/**
+ * wakeup_readers - wake up readers waiting on a channel
+ * @private: the channel buffer
+ *
+ * This is the work function used to defer reader waking. The
+ * reason waking is deferred is that calling directly from write
+ * causes problems if you're writing from say the scheduler.
+ */
+static void wakeup_readers(void *private)
+{
+ struct rchan_buf *buf = private;
+ wake_up_interruptible(&buf->read_wait);
+}
+
+/**
+ * get_next_subbuf - return next sub-buffer within channel buffer
+ * @buf: channel buffer
+ */
+static inline void *get_next_subbuf(struct rchan_buf *buf)
+{
+ void *next = buf->data + buf->chan->subbuf_size;
+ if (next >= buf->start + buf->chan->subbuf_size * buf->chan->n_subbufs)
+ next = buf->start;
+
+ return next;
+}
+
+/**
+ * __relay_reset - reset a channel buffer
+ * @buf: the channel buffer
+ * @init: 1 if this is a first-time initialization
+ *
+ * See relay_reset for description of effect.
+ */
+static inline void __relay_reset(struct rchan_buf *buf, int init)
+{
+ int i;
+
+ if (init) {
+ init_waitqueue_head(&buf->read_wait);
+ kref_init(&buf->kref);
+ }
+
+ atomic_set(&buf->subbufs_produced, 0);
+ atomic_set(&buf->subbufs_consumed, 0);
+ atomic_set(&buf->unfull, 0);
+ buf->finalized = 0;
+ buf->data = buf->start;
+ buf->offset = 0;
+
+ for (i = 0; i < buf->chan->n_subbufs; i++) {
+ buf->padding[i] = 0;
+ buf->commit[i] = 0;
+ }
+
+ buf->offset = buf->chan->cb->subbuf_start(buf, buf->data, 0, NULL);
+ buf->commit[0] = buf->offset;
+
+ INIT_WORK(&buf->wake_readers, NULL, NULL);
+}
+
+/**
+ * relay_reset - reset the channel
+ * @chan: the channel
+ *
+ * Returns 0 if successful, negative if not.
+ *
+ * This has the effect of erasing all data from all channel buffers
+ * and restarting the channel in its initial state. The buffers
+ * are not freed, so any mappings are still in effect.
+ *
+ * NOTE: Care should be taken that the channel isn't actually
+ * being used by anything when this call is made.
+ */
+void relay_reset(struct rchan *chan)
+{
+ int i;
+
+ if (!chan)
+ return;
+
+ for (i = 0; i < NR_CPUS; i++) {
+ if (!chan->buf[i])
+ continue;
+ __relay_reset(chan->buf[i], 0);
+ }
+}
+
+/**
+ * relay_open_buf - create a new channel buffer in relayfs
+ *
+ * Internal - used by relay_open().
+ */
+static struct rchan_buf *relay_open_buf(struct rchan *chan,
+ const char *filename,
+ struct dentry *parent)
+{
+ struct rchan_buf *buf;
+ struct dentry *dentry;
+
+ /* Create file in fs */
+ dentry = relayfs_create_file(filename, parent, S_IRUSR, chan);
+ if (!dentry)
+ return NULL;
+
+ buf = RELAYFS_I(dentry->d_inode)->buf;
+ buf->dentry = dentry;
+ __relay_reset(buf, 1);
+
+ return buf;
+}
+
+/**
+ * relay_close_buf - close a channel buffer
+ * @buf: channel buffer
+ *
+ * Marks the buffer finalized and restores the default callbacks.
+ * The channel buffer and channel buffer data structure are then freed
+ * automatically when the last reference is given up.
+ */
+static inline void relay_close_buf(struct rchan_buf *buf)
+{
+ buf->finalized = 1;
+ buf->chan->cb = &default_channel_callbacks;
+ kref_put(&buf->kref, relay_remove_buf);
+}
+
+static inline void setup_callbacks(struct rchan *chan,
+ struct rchan_callbacks *cb)
+{
+ if (!cb) {
+ chan->cb = &default_channel_callbacks;
+ return;
+ }
+
+ if (!cb->subbuf_start)
+ cb->subbuf_start = subbuf_start_default_callback;
+ if (!cb->deliver)
+ cb->deliver = deliver_default_callback;
+ if (!cb->buf_mapped)
+ cb->buf_mapped = buf_mapped_default_callback;
+ if (!cb->buf_unmapped)
+ cb->buf_unmapped = buf_unmapped_default_callback;
+ if (!cb->buf_full)
+ cb->buf_full = buf_full_default_callback;
+ chan->cb = cb;
+}
+
+/**
+ * relay_open - create a new relayfs channel
+ * @base_filename: base name of files to create
+ * @parent: dentry of parent directory, NULL for root directory
+ * @subbuf_size: size of sub-buffers
+ * @n_subbufs: number of sub-buffers
+ * @overwrite: overwrite buffer when full?
+ * @cb: client callback functions
+ *
+ * Returns channel pointer if successful, NULL otherwise.
+ *
+ * Creates a channel buffer for each cpu using the sizes and
+ * attributes specified. The created channel buffer files
+ * will be named base_filename0...base_filenameN-1. File
+ * permissions will be S_IRUSR.
+ */
+struct rchan *relay_open(const char *base_filename,
+ struct dentry *parent,
+ unsigned subbuf_size,
+ unsigned n_subbufs,
+ int overwrite,
+ struct rchan_callbacks *cb)
+{
+ int i;
+ struct rchan *chan;
+ char *tmpname;
+
+ if (!base_filename)
+ return NULL;
+
+ if (!(subbuf_size && n_subbufs))
+ return NULL;
+
+ chan = kcalloc(1, sizeof(struct rchan), GFP_KERNEL);
+ if (!chan)
+ return NULL;
+
+ chan->version = RELAYFS_CHANNEL_VERSION;
+ chan->overwrite = overwrite;
+ chan->n_subbufs = n_subbufs;
+ chan->subbuf_size = subbuf_size;
+ chan->alloc_size = FIX_SIZE(subbuf_size * n_subbufs);
+ setup_callbacks(chan, cb);
+ kref_init(&chan->kref);
+
+ tmpname = kmalloc(NAME_MAX + 1, GFP_KERNEL);
+ if (!tmpname)
+ goto free_chan;
+
+ for_each_online_cpu(i) {
+ sprintf(tmpname, "%s%d", base_filename, i);
+ chan->buf[i] = relay_open_buf(chan, tmpname, parent);
+ if (!chan->buf[i])
+ goto free_bufs;
+ }
+
+ kfree(tmpname);
+ return chan;
+
+free_bufs:
+ for (i = 0; i < NR_CPUS; i++) {
+ if (!chan->buf[i])
+ break;
+ relay_close_buf(chan->buf[i]);
+ }
+ kfree(tmpname);
+
+free_chan:
+ kref_put(&chan->kref, relay_destroy_channel);
+ return NULL;
+}
+
+/**
+ * deliver_check - deliver a guaranteed full sub-buffer if applicable
+ */
+static inline void deliver_check(struct rchan_buf *buf,
+ unsigned subbuf_idx)
+{
+ void *subbuf;
+ unsigned full = buf->chan->subbuf_size - buf->padding[subbuf_idx];
+
+ if (buf->commit[subbuf_idx] == full) {
+ subbuf = buf->start + subbuf_idx * buf->chan->subbuf_size;
+ buf->chan->cb->deliver(buf, subbuf_idx, subbuf);
+ }
+}
+
+/**
+ * do_switch - change subbuf pointer and do related bookkeeping
+ */
+static inline void do_switch(struct rchan_buf *buf, unsigned new, unsigned old)
+{
+ unsigned start = 0;
+ void *old_data = buf->start + old * buf->chan->subbuf_size;
+
+ buf->data = get_next_subbuf(buf);
+ buf->padding[new] = 0;
+ start = buf->chan->cb->subbuf_start(buf, buf->data, old, old_data);
+ buf->offset = buf->commit[new] = start;
+}
+
+/**
+ * relay_switch_subbuf - switch to a new sub-buffer
+ * @buf: channel buffer
+ * @length: size of current event
+ *
+ * Returns either the length passed in or 0 if full.
+
+ * Performs sub-buffer-switch tasks such as invoking callbacks,
+ * updating padding counts, waking up readers, etc.
+ */
+unsigned relay_switch_subbuf(struct rchan_buf *buf, unsigned length)
+{
+ int new, old, produced = atomic_read(&buf->subbufs_produced);
+ unsigned padding;
+
+ if (atomic_read(&buf->unfull)) {
+ atomic_set(&buf->unfull, 0);
+ new = produced % buf->chan->n_subbufs;
+ old = (produced - 1) % buf->chan->n_subbufs;
+ do_switch(buf, new, old);
+ return 0;
+ }
+
+ if (unlikely(relay_buf_full(buf)))
+ return 0;
+
+ old = produced % buf->chan->n_subbufs;
+ padding = buf->chan->subbuf_size - buf->offset;
+ buf->padding[old] = padding;
+ deliver_check(buf, old);
+ buf->offset = buf->chan->subbuf_size;
+ atomic_inc(&buf->subbufs_produced);
+
+ if (waitqueue_active(&buf->read_wait)) {
+ PREPARE_WORK(&buf->wake_readers, wakeup_readers, buf);
+ schedule_delayed_work(&buf->wake_readers, 1);
+ }
+
+ if (unlikely(relay_buf_full(buf))) {
+ void *old_data = buf->start + old * buf->chan->subbuf_size;
+ buf->chan->cb->buf_full(buf, old, old_data);
+ return 0;
+ }
+
+ new = (produced + 1) % buf->chan->n_subbufs;
+ do_switch(buf, new, old);
+
+ return length;
+}
+
+/**
+ * relay_commit - add count bytes to a sub-buffer's commit count
+ * @buf: channel buffer
+ * @reserved: reserved address associated with commit
+ * @count: number of bytes committed
+ *
+ * Invokes deliver() callback if sub-buffer is completely written.
+ */
+void relay_commit(struct rchan_buf *buf,
+ void *reserved,
+ unsigned count)
+{
+ unsigned offset, subbuf_idx;
+
+ offset = reserved - buf->start;
+ subbuf_idx = offset / buf->chan->subbuf_size;
+ buf->commit[subbuf_idx] += count;
+ deliver_check(buf, subbuf_idx);
+}
+
+/**
+ * relay_subbufs_consumed - update the buffer's sub-buffers-consumed count
+ * @chan: the channel
+ * @cpu: the cpu associated with the channel buffer to update
+ * @subbufs_consumed: number of sub-buffers to add to current buf's count
+ *
+ * Adds to the channel buffer's consumed sub-buffer count.
+ * subbufs_consumed should be the number of sub-buffers newly consumed,
+ * not the total consumed.
+ *
+ * NOTE: kernel clients don't need to call this function if the channel
+ * mode is 'overwrite'.
+ */
+void relay_subbufs_consumed(struct rchan *chan, int cpu, int subbufs_consumed)
+{
+ int produced, consumed;
+ struct rchan_buf *buf;
+
+ if (!chan)
+ return;
+
+ if (cpu >= NR_CPUS || !chan->buf[cpu])
+ return;
+
+ buf = chan->buf[cpu];
+ if (relay_buf_full(buf))
+ atomic_set(&buf->unfull, 1);
+
+ atomic_add(subbufs_consumed, &buf->subbufs_consumed);
+ produced = atomic_read(&buf->subbufs_produced);
+ consumed = atomic_read(&buf->subbufs_consumed);
+ if (consumed > produced)
+ atomic_set(&buf->subbufs_consumed, produced);
+}
+
+/**
+ * relay_destroy_channel - free the channel struct
+ *
+ * Should only be called from kref_put().
+ */
+void relay_destroy_channel(struct kref *kref)
+{
+ struct rchan *chan = container_of(kref, struct rchan, kref);
+ kfree(chan);
+}
+
+/**
+ * relay_close - close the channel
+ * @chan: the channel
+ *
+ * Closes all channel buffers and frees the channel.
+ */
+void relay_close(struct rchan *chan)
+{
+ int i;
+
+ if (!chan)
+ return;
+
+ for (i = 0; i < NR_CPUS; i++) {
+ if (!chan->buf[i])
+ continue;
+ relay_close_buf(chan->buf[i]);
+ }
+
+ kref_put(&chan->kref, relay_destroy_channel);
+}
+
+/**
+ * relay_flush - close the channel
+ * @chan: the channel
+ *
+ * Flushes all channel buffers i.e. forces buffer switch.
+ */
+void relay_flush(struct rchan *chan)
+{
+ int i;
+
+ if (!chan)
+ return;
+
+ for (i = 0; i < NR_CPUS; i++) {
+ if (!chan->buf[i])
+ continue;
+ relay_switch_subbuf(chan->buf[i], 0);
+ }
+}
+
+EXPORT_SYMBOL_GPL(relay_open);
+EXPORT_SYMBOL_GPL(relay_close);
+EXPORT_SYMBOL_GPL(relay_flush);
+EXPORT_SYMBOL_GPL(relay_reset);
+EXPORT_SYMBOL_GPL(relay_subbufs_consumed);
+EXPORT_SYMBOL_GPL(relay_commit);
+EXPORT_SYMBOL_GPL(relay_switch_subbuf);
diff --git a/runtime/relayfs/relay.h b/runtime/relayfs/relay.h
new file mode 100644
index 00000000..703503fa
--- /dev/null
+++ b/runtime/relayfs/relay.h
@@ -0,0 +1,12 @@
+#ifndef _RELAY_H
+#define _RELAY_H
+
+struct dentry *relayfs_create_file(const char *name,
+ struct dentry *parent,
+ int mode,
+ struct rchan *chan);
+extern int relayfs_remove(struct dentry *dentry);
+extern int relay_buf_empty(struct rchan_buf *buf);
+extern void relay_destroy_channel(struct kref *kref);
+
+#endif /* _RELAY_H */
diff --git a/runtime/relayfs/relayfs.txt b/runtime/relayfs/relayfs.txt
new file mode 100644
index 00000000..ada829bb
--- /dev/null
+++ b/runtime/relayfs/relayfs.txt
@@ -0,0 +1,206 @@
+
+relayfs - a high-speed data relay filesystem
+============================================
+
+relayfs is a filesystem designed to provide an efficient mechanism for
+tools and facilities to relay large and potentially sustained streams
+of data from kernel space to user space.
+
+The main abstraction of relayfs is the 'channel'. A channel consists
+of a set of per-cpu kernel buffers each represented by a file in the
+relayfs filesystem. Kernel clients write into a channel using
+efficient write functions which automatically log to the current cpu's
+channel buffer. User space applications mmap() the per-cpu files and
+retrieve the data as it becomes available.
+
+The format of the data logged into the channel buffers is completely
+up to the relayfs client; relayfs does however provide hooks which
+allow clients to impose some stucture on the buffer data. Nor does
+relayfs implement any form of data filtering - this also is left to
+the client. The purpose is to keep relayfs as simple as possible.
+
+This document provides an overview of the relayfs API. The details of
+the function parameters are documented along with the functions in the
+filesystem code - please see that for details.
+
+
+The relayfs user space API
+==========================
+
+relayfs implements basic file operations for user space access to
+relayfs channel buffer data. Here are the file operations that are
+available and some comments regarding their behavior:
+
+open() enables user to open an _existing_ buffer.
+
+mmap() results in channel buffer being mapped into the caller's
+ memory space.
+
+poll() POLLIN/POLLRDNORM/POLLERR supported. User applications are
+ notified when sub-buffer boundaries are crossed.
+
+close() decrements the channel buffer's refcount. When the refcount
+ reaches 0 i.e. when no process or kernel client has the buffer
+ open, the channel buffer is freed.
+
+
+In order for a user application to make use of relayfs files, the
+relayfs filesystem must be mounted. For example,
+
+ mount -t relayfs relayfs /mnt/relay
+
+NOTE: relayfs doesn't need to be mounted for kernel clients to create
+ or use channels - it only needs to be mounted when user space
+ applications need access to the buffer data.
+
+
+The relayfs kernel API
+======================
+
+Here's a summary of the API relayfs provides to in-kernel clients:
+
+
+ channel management functions:
+
+ relay_open(base_filename, parent, subbuf_size, n_subbufs,
+ overwrite, callbacks)
+ relay_close(chan)
+ relay_flush(chan)
+ relay_reset(chan)
+ relayfs_create_dir(name, parent)
+ relayfs_remove_dir(dentry)
+ relay_commit(buf, reserved, count)
+ relay_subbufs_consumed(chan, cpu, subbufs_consumed)
+
+ write functions:
+
+ relay_write(chan, data, length)
+ __relay_write(chan, data, length)
+ relay_reserve(chan, length)
+
+ callbacks:
+
+ subbuf_start(buf, subbuf, prev_subbuf_idx, prev_subbuf)
+ deliver(buf, subbuf_idx, subbuf)
+ buf_mapped(buf, filp)
+ buf_unmapped(buf, filp)
+ buf_full(buf, subbuf_idx)
+
+
+A relayfs channel is made of up one or more per-cpu channel buffers,
+each implemented as a circular buffer subdivided into one or more
+sub-buffers.
+
+relay_open() is used to create a channel, along with its per-cpu
+channel buffers. Each channel buffer will have an associated file
+created for it in the relayfs filesystem, which can be opened and
+mmapped from user space if desired. The files are named
+basename0...basenameN-1 where N is the number of online cpus, and by
+default will be created in the root of the filesystem. If you want a
+directory structure to contain your relayfs files, you can create it
+with relayfs_create_dir() and pass the parent directory to
+relay_open(). Clients are responsible for cleaning up any directory
+structure they create when the channel is closed - use
+relayfs_remove_dir() for that.
+
+The total size of each per-cpu buffer is calculated by multiplying the
+number of sub-buffers by the sub-buffer size passed into relay_open().
+The idea behind sub-buffers is that they're basically an extension of
+double-buffering to N buffers, and they also allow applications to
+easily implement random-access-on-buffer-boundary schemes, which can
+be important for some high-volume applications. The number and size
+of sub-buffers is completely dependent on the application and even for
+the same application, different conditions will warrant different
+values for these parameters at different times. Typically, the right
+values to use are best decided after some experimentation; in general,
+though, it's safe to assume that having only 1 sub-buffer is a bad
+idea - you're guaranteed to either overwrite data or lose events
+depending on the channel mode being used.
+
+relayfs channels can be opened in either of two modes - 'overwrite' or
+'no-overwrite'. In overwrite mode, writes continuously cycle around
+the buffer and will never fail, but will unconditionally overwrite old
+data regardless of whether it's actually been consumed. In
+no-overwrite mode, writes will fail i.e. data will be lost, if the
+number of unconsumed sub-buffers equals the total number of
+sub-buffers in the channel. In this mode, the client is reponsible
+for notifying relayfs when sub-buffers have been consumed via
+relay_subbufs_consumed(). A full buffer will become 'unfull' and
+logging will continue once the client calls relay_subbufs_consumed()
+again. When a buffer becomes full, the buf_full() callback is invoked
+to notify the client. In both modes, the subbuf_start() callback will
+notify the client whenever a sub-buffer boundary is crossed. This can
+be used to write header information into the new sub-buffer or fill in
+header information reserved in the previous sub-buffer. One piece of
+information that's useful to save in a reserved header slot is the
+number of bytes of 'padding' for a sub-buffer, which is the amount of
+unused space at the end of a sub-buffer. The padding count for each
+sub-buffer is contained in an array in the rchan_buf struct passed
+into the subbuf_start() callback: rchan_buf->padding[prev_subbuf_idx]
+can be used to to get the padding for the just-finished sub-buffer.
+subbuf_start() is also called for the first sub-buffer in each channel
+buffer when the channel is created. The mode is specified to
+relay_open() using the overwrite parameter.
+
+kernel clients write data into the current cpu's channel buffer using
+relay_write() or __relay_write(). relay_write() is the main logging
+function - it uses local_irqsave() to protect the buffer and should be
+used if you might be logging from interrupt context. If you know
+you'll never be logging from interrupt context, you can use
+__relay_write(), which only disables preemption. These functions
+don't return a value, so you can't determine whether or not they
+failed - the assumption is that you wouldn't want to check a return
+value in the fast logging path anyway, and that they'll always succeed
+unless the buffer is full and in no-overwrite mode, in which case
+you'll be notified via the buf_full() callback.
+
+relay_reserve() is used to reserve a slot in a channel buffer which
+can be written to later. This would typically be used in applications
+that need to write directly into a channel buffer without having to
+stage data in a temporary buffer beforehand. Because the actual write
+may not happen immediately after the slot is reserved, applications
+using relay_reserve() can call relay_commit() to notify relayfs when
+the slot has actually been written. When all the reserved slots have
+been committed, the deliver() callback is invoked to notify the client
+that a guaranteed full sub-buffer has been produced. Because the
+write is under control of the client and is separated from the
+reserve, relay_reserve() doesn't protect the buffer at all - it's up
+to the client to provide the appropriate synchronization when using
+relay_reserve().
+
+The client calls relay_close() when it's finished using the channel.
+The channel and its associated buffers are destroyed when there are no
+longer any references to any of the channel buffers. relay_flush()
+forces a sub-buffer switch on all the channel buffers, and can be used
+to finalize and process the last sub-buffers before the channel is
+closed.
+
+Some applications may want to keep a channel around and re-use it
+rather than open and close a new channel for each use. relay_reset()
+can be used for this purpose - it resets a channel to its initial
+state without reallocating channel buffer memory or destroying
+existing mappings. It should however only be called when it's safe to
+do so i.e. when the channel isn't currently being written to.
+
+Finally, there are a couple of utility callbacks that can be used for
+different purposes. buf_mapped() is called whenever a channel buffer
+is mmapped from user space and buf_unmapped() is called when it's
+unmapped. The client can use this notification to trigger actions
+within the kernel application, such as enabling/disabling logging to
+the channel.
+
+
+Credits
+=======
+
+The ideas and specs for relayfs came about as a result of discussions
+on tracing involving the following:
+
+Michel Dagenais <michel.dagenais@polymtl.ca>
+Richard Moore <richardj_moore@uk.ibm.com>
+Bob Wisniewski <bob@watson.ibm.com>
+Karim Yaghmour <karim@opersys.com>
+Tom Zanussi <zanussi@us.ibm.com>
+
+Also thanks to Hubertus Franke for a lot of useful suggestions and bug
+reports.
diff --git a/runtime/runtime.h b/runtime/runtime.h
index f97c970a..ee92c8dc 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -21,4 +21,6 @@
#define dbug(args...) ;
+#include "print.c"
+
#endif /* _RUNTIME_H_ */
diff --git a/runtime/scbuf.c b/runtime/scbuf.c
deleted file mode 100644
index 15458bee..00000000
--- a/runtime/scbuf.c
+++ /dev/null
@@ -1,71 +0,0 @@
-#ifndef _SCBUF_C_
-#define _SCBUF_C_
-
-/* -*- linux-c -*- */
-/** @file scbuf.c
- * @addtogroup scbuf Scratch Buffer
- * Scratch Buffer Functions.
- * The scratch buffer is for collecting output before storing in a map,
- * printing, etc. This is a per-cpu static buffer. It is necessary because
- * of the limited stack space available in the kernel.
- * @{
- */
-
-/** Maximum size of buffer, not including terminating NULL */
-#define STP_BUF_LEN 8191
-
-/** Scratch buffer for printing, building strings, etc */
-char _stp_scbuf[STP_BUF_LEN+1];
-static int _stp_scbuf_len = STP_BUF_LEN;
-
-/** Sprint into the scratch buffer.
- * Like printf, except output goes into #_stp_scbuf,
- * which will contain the null-terminated output.
- * Safe because overflowing #_stp_scbuf is not allowed.
- * Size is limited by length of scratch buffer, STP_BUF_LEN.
- *
- * @param fmt A printf-style format string followed by a
- * variable number of args.
- * @sa _stp_scbuf_clear
- */
-
-void _stp_sprint (const char *fmt, ...)
-{
- int num;
- va_list args;
- char *buf = _stp_scbuf + STP_BUF_LEN - _stp_scbuf_len;
- va_start(args, fmt);
- num = vscnprintf(buf, _stp_scbuf_len, fmt, args);
- va_end(args);
- if (num > 0)
- _stp_scbuf_len -= num;
-}
-
-void _stp_sprint_str (const char *str)
-{
- char *buf = _stp_scbuf + STP_BUF_LEN - _stp_scbuf_len;
- int num = strlen (str);
- if (num > _stp_scbuf_len)
- num = _stp_scbuf_len;
- strncpy (buf, str, num);
- _stp_scbuf_len -= num;
-}
-
-/** Clear the scratch buffer.
- * Output from _stp_sprint() will accumulate in the buffer
- * until this is called.
- */
-
-void _stp_scbuf_clear (void)
-{
- _stp_scbuf_len = STP_BUF_LEN;
- _stp_scbuf[0] = 0;
-}
-
-static char *_stp_scbuf_cur (void)
-{
- return _stp_scbuf + STP_BUF_LEN - _stp_scbuf_len;
-}
-
-/** @} */
-#endif /* _SCBUF_C_ */
diff --git a/runtime/stack.c b/runtime/stack.c
index 66fc6699..758013cb 100644
--- a/runtime/stack.c
+++ b/runtime/stack.c
@@ -1,6 +1,6 @@
-#ifndef _STACK_C_
+#ifndef _STACK_C_ /* -*- linux-c -*- */
#define _STACK_C_
-/* -*- linux-c -*- */
+
/** @file stack.c
* @brief Stack Tracing Functions
@@ -20,35 +20,34 @@ static void __stp_stack_print (unsigned long *stack, int verbose, int levels)
unsigned long addr;
if (verbose)
- _stp_print ("trace for %d (%s)\n", current->pid, current->comm);
+ _stp_printf ("trace for %d (%s)\n", current->pid, current->comm);
while (((long) stack & (THREAD_SIZE-1)) != 0) {
addr = *stack++;
if (_stp_kta(addr)) {
- if (verbose)
+ if (verbose) {
_stp_symbol_print (addr);
- else
- _stp_print ("0x%lx ", addr);
+ _stp_print ("\n");
+ } else
+ _stp_printf ("0x%lx ", addr);
}
}
- _stp_print_str ("\n");
+ _stp_print_flush();
}
-static char *__stp_stack_sprint (unsigned long *stack, int verbose, int levels)
+static void __stp_stack_sprint (String str, unsigned long *stack, int verbose, int levels)
{
unsigned long addr;
- char *ptr = _stp_scbuf_cur();
while (((long) stack & (THREAD_SIZE-1)) != 0) {
addr = *stack++;
if (_stp_kta(addr)) {
if (verbose)
- _stp_symbol_sprint (addr);
+ _stp_symbol_sprint (str, addr);
else
- _stp_sprint ("0x%lx ", addr);
+ _stp_sprintf (str, "0x%lx ", addr);
}
}
- return ptr;
}
#else /* i386 */
@@ -70,7 +69,7 @@ static inline unsigned long _stp_print_context_stack (
while (valid_stack_ptr(tinfo, (void *)ebp)) {
addr = *(unsigned long *)(ebp + 4);
_stp_symbol_print (addr);
- _stp_print_str("\n");
+ _stp_print_cstr("\n");
ebp = *(unsigned long *)ebp;
}
#else
@@ -78,14 +77,16 @@ static inline unsigned long _stp_print_context_stack (
addr = *stack++;
if (_stp_kta (addr)) {
_stp_symbol_print (addr);
- _stp_print_str ("\n");
+ _stp_print_cstr ("\n");
}
}
#endif
+ _stp_print_flush();
return ebp;
}
static inline unsigned long _stp_sprint_context_stack (
+ String str,
struct thread_info *tinfo,
unsigned long *stack,
unsigned long ebp )
@@ -95,8 +96,8 @@ static inline unsigned long _stp_sprint_context_stack (
#ifdef CONFIG_FRAME_POINTER
while (valid_stack_ptr(tinfo, (void *)ebp)) {
addr = *(unsigned long *)(ebp + 4);
- _stp_symbol_sprint (addr);
- _stp_sprint_str("\n");
+ _stp_symbol_sprint (str, addr);
+ _stp_string_cat ("\n");
ebp = *(unsigned long *)ebp;
}
#else
@@ -104,7 +105,7 @@ static inline unsigned long _stp_sprint_context_stack (
addr = *stack++;
if (_stp_kta (addr)) {
_stp_symbol_sprint (addr);
- _stp_sprint_str ("\n");
+ _stp_string_cat ("\n");
}
}
#endif
@@ -126,7 +127,7 @@ static void __stp_stack_print (unsigned long *stack, int verbose, int levels)
}
}
-static void __stp_stack_sprint (unsigned long *stack, int verbose, int levels)
+static void __stp_stack_sprint (String str, unsigned long *stack, int verbose, int levels)
{
unsigned long ebp;
@@ -136,25 +137,30 @@ static void __stp_stack_sprint (unsigned long *stack, int verbose, int levels)
while (stack) {
struct thread_info *context = (struct thread_info *)
((unsigned long)stack & (~(THREAD_SIZE - 1)));
- ebp = _stp_sprint_context_stack (context, stack, ebp);
+ ebp = _stp_sprint_context_stack (str, context, stack, ebp);
stack = (unsigned long*)context->previous_esp;
}
}
#endif /* i386 */
+/** Print stack dump.
+ * Prints a stack dump to the trace buffer.
+ * @param verbose Verbosity:
+ */
+
void _stp_stack_print (int verbose, int levels)
{
unsigned long stack;
- return __stp_stack_print (&stack, verbose, levels);
+ __stp_stack_print (&stack, verbose, levels);
}
-char *_stp_stack_sprint (int verbose, int levels)
+String _stp_stack_sprint (String str, int verbose, int levels)
{
unsigned long stack;
- char *ptr = _stp_scbuf_cur();
- __stp_stack_sprint (&stack, verbose, levels);
- return ptr;
+ __stp_stack_sprint (str, &stack, verbose, levels);
+ _stp_log ("sss: str=%s\n", str->buf);
+ return str;
}
/** @} */
diff --git a/runtime/stpd/Makefile b/runtime/stpd/Makefile
new file mode 100644
index 00000000..0c41c439
--- /dev/null
+++ b/runtime/stpd/Makefile
@@ -0,0 +1,7 @@
+all: stpd
+
+stpd: stpd.c librelay.c
+ $(CC) -Wall -O3 -o stpd stpd.c librelay.c -lpthread
+
+clean:
+ /bin/rm -f stpd stpd.o librelay.o *~
diff --git a/runtime/stpd/librelay.c b/runtime/stpd/librelay.c
new file mode 100644
index 00000000..04d1ba6b
--- /dev/null
+++ b/runtime/stpd/librelay.c
@@ -0,0 +1,520 @@
+/*
+ * librelay - relay-app user space 'library'
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2005 - Tom Zanussi (zanussi@us.ibm.com), IBM Corp
+ *
+ */
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <linux/fd.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <pthread.h>
+#include <sys/socket.h>
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include "librelay.h"
+
+/* maximum number of CPUs we can handle - change if more */
+#define NR_CPUS 256
+
+ /* internal variables */
+static unsigned subbuf_size;
+static unsigned n_subbufs;
+static int ncpus;
+static int print_totals;
+static int logging;
+
+static int relay_file[NR_CPUS];
+static int out_file[NR_CPUS];
+static char *relay_buffer[NR_CPUS];
+
+ /* netlink control channel */
+static int control_channel;
+static int nl_unit;
+
+/* flags */
+extern int print_only;
+extern int quiet;
+
+/* used to communicate with kernel over control channel */
+
+struct buf_info
+{
+ int cpu;
+ unsigned produced;
+ unsigned consumed;
+};
+
+struct consumed_info
+{
+ int cpu;
+ unsigned consumed;
+};
+
+struct channel_create_info
+{
+ unsigned subbuf_size;
+ unsigned n_subbufs;
+};
+
+struct app_msg
+{
+ struct nlmsghdr nlh;
+ struct buf_info info;
+};
+
+static char *recvbuf[8192];
+
+/* per-cpu buffer info */
+static struct buf_status
+{
+ pthread_mutex_t ready_mutex;
+ pthread_cond_t ready_cond;
+ struct buf_info info;
+ unsigned max_backlog; /* max # sub-buffers ready at one time */
+} status[NR_CPUS];
+
+/* colors for printing */
+static char *color[] = {
+ "\033[31m", /* red */
+ "\033[32m", /* green */
+ "\033[33m", /* yellow */
+ "\033[34m", /* blue */
+ "\033[35m", /* magenta */
+ "\033[36m", /* cyan */
+};
+
+
+enum
+{
+ STP_REALTIME_DATA = RELAY_APP_USERCMD_START,
+ STP_EXIT,
+ STP_DONE
+};
+
+
+/**
+ * send_request - send request to kernel over control channel
+ * @type: the relay-app command id
+ * @data: pointer to the data to be sent
+ * @len: length of the data to be sent
+ *
+ * Returns 0 on success, negative otherwise.
+ */
+int send_request(int type, void *data, int len)
+{
+ struct nlmsghdr *req;
+ int err;
+
+ req = (struct nlmsghdr *)malloc(NLMSG_SPACE(len));
+ memset(req, 0, NLMSG_SPACE(len));
+ req->nlmsg_len = NLMSG_LENGTH(len);
+ req->nlmsg_type = type;
+ req->nlmsg_flags = NLM_F_REQUEST;
+ req->nlmsg_pid = getpid();
+ memcpy(NLMSG_DATA(req), data, len);
+
+ err = send(control_channel, req, req->nlmsg_len, 0);
+#if 0
+ if (err < 0)
+ fprintf(stderr, "netlink send error\n");
+#endif
+ return err;
+}
+
+/**
+ * open_control_channel - create netlink channel
+ */
+static int open_control_channel()
+{
+ struct sockaddr_nl snl;
+ int channel;
+
+ channel = socket(AF_NETLINK, SOCK_RAW, nl_unit);
+ if (channel < 0) {
+ printf("socket() failed\n");
+ return channel;
+ }
+
+ memset(&snl, 0, sizeof snl);
+ snl.nl_family = AF_NETLINK;
+ snl.nl_pid = getpid();
+ snl.nl_groups = 0;
+
+ if (bind (channel, (struct sockaddr *) &snl, sizeof snl))
+ printf("bind() failed\n");
+
+ return channel;
+}
+
+/**
+ * process_subbufs - write ready subbufs to disk and/or screen
+ */
+
+static int process_subbufs (struct buf_info *info)
+{
+ unsigned subbufs_ready, start_subbuf, end_subbuf, subbuf_idx;
+ int i, len, cpu = info->cpu;
+ char *subbuf_ptr;
+ int subbufs_consumed = 0;
+ unsigned padding;
+
+ subbufs_ready = info->produced - info->consumed;
+ start_subbuf = info->consumed % n_subbufs;
+ end_subbuf = start_subbuf + subbufs_ready;
+
+ if (!quiet)
+ fputs ( color[cpu % 4], stdout);
+
+ for (i = start_subbuf; i < end_subbuf; i++) {
+ subbuf_idx = i % n_subbufs;
+ subbuf_ptr = relay_buffer[cpu] + subbuf_idx * subbuf_size;
+ padding = *((unsigned *)subbuf_ptr);
+ subbuf_ptr += sizeof(padding);
+ len = (subbuf_size - sizeof(padding)) - padding;
+
+ if (!print_only)
+ {
+ if (write(out_file[cpu], subbuf_ptr, len) < 0)
+ {
+ printf("Couldn't write to output file for cpu %d, exiting: errcode = %d: %s\n", cpu, errno, strerror(errno));
+ exit(1);
+ }
+ }
+
+ if (!quiet)
+ fwrite (subbuf_ptr, len, 1, stdout);
+
+ subbufs_consumed++;
+ }
+
+ return subbufs_consumed;
+}
+
+
+
+/**
+ * reader_thread - per-cpu channel buffer reader
+ */
+static void *reader_thread(void *data)
+{
+ int rc;
+ long cpu = (long)data;
+ struct pollfd pollfd;
+ struct consumed_info consumed_info;
+ unsigned subbufs_consumed;
+
+ do {
+ pollfd.fd = relay_file[cpu];
+ pollfd.events = POLLIN;
+ rc = poll(&pollfd, 1, -1);
+ if (rc < 0) {
+ if (errno != EINTR) {
+ printf("poll error: %s\n",strerror(errno));
+ exit(1);
+ }
+ printf("poll warning: %s\n",strerror(errno));
+ rc = 0;
+ }
+
+ send_request(RELAY_APP_BUF_INFO, &status[cpu].info,
+ sizeof(struct buf_info));
+ if (status[cpu].info.produced == status[cpu].info.consumed)
+ pthread_cond_wait(&status[cpu].ready_cond,
+ &status[cpu].ready_mutex);
+ pthread_mutex_unlock(&status[cpu].ready_mutex);
+
+ subbufs_consumed = process_subbufs(&status[cpu].info);
+ if (subbufs_consumed) {
+ if (subbufs_consumed == n_subbufs)
+ fprintf(stderr, "cpu %ld buffer full. Consider using a larger buffer size.\n", cpu);
+ if (subbufs_consumed > status[cpu].max_backlog)
+ status[cpu].max_backlog = subbufs_consumed;
+ status[cpu].info.consumed += subbufs_consumed;
+ consumed_info.cpu = cpu;
+ consumed_info.consumed = subbufs_consumed;
+ send_request(RELAY_APP_SUBBUFS_CONSUMED,
+ &consumed_info,
+ sizeof(struct consumed_info));
+ }
+ } while (1);
+}
+
+static void summarize(void)
+{
+ int i;
+
+ printf("summary:\n");
+ for (i = 0; i < ncpus; i++) {
+ printf("%s cpu %u:\n", color[i % 4], i);
+ printf(" %u sub-buffers processed\n",
+ status[i].info.consumed);
+ printf(" %u max backlog\n", status[i].max_backlog);
+ }
+}
+
+/**
+ * close_files - close and munmap buffer and open output file
+ */
+static void close_files(int cpu)
+{
+ size_t total_bufsize = subbuf_size * n_subbufs;
+
+ munmap(relay_buffer[cpu], total_bufsize);
+ close(relay_file[cpu]);
+ if (!print_only) close(out_file[cpu]);
+}
+
+static void close_all_files(void)
+{
+ int i;
+
+ for (i = 0; i < ncpus; i++)
+ close_files(i);
+}
+
+static void sigalarm(int signum)
+{
+ if (print_totals)
+ summarize();
+ close_all_files();
+ send_request(RELAY_APP_CHAN_DESTROY, NULL, 0);
+ exit(0);
+}
+
+static void sigproc(int signum)
+{
+ send_request(STP_EXIT, NULL, 0);
+}
+
+/**
+ * open_files - open and mmap buffer and open output file
+ */
+static int open_files(int cpu, const char *relay_filebase,
+ const char *out_filebase)
+{
+ size_t total_bufsize;
+ char tmp[4096];
+
+ memset(&status[cpu], 0, sizeof(struct buf_status));
+ pthread_mutex_init(&status[cpu].ready_mutex, NULL);
+ pthread_cond_init(&status[cpu].ready_cond, NULL);
+ status[cpu].info.cpu = cpu;
+
+ sprintf(tmp, "%s%d", relay_filebase, cpu);
+ relay_file[cpu] = open(tmp, O_RDONLY | O_NONBLOCK);
+ if (relay_file[cpu] < 0) {
+ printf("Couldn't open relayfs file %s: errcode = %s\n",
+ tmp, strerror(errno));
+ return -1;
+ }
+
+ if (!print_only) {
+ sprintf(tmp, "%s%d", out_filebase, cpu);
+ if((out_file[cpu] = open(tmp, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR |
+ S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
+ printf("Couldn't open output file %s: errcode = %s\n",
+ tmp, strerror(errno));
+ close(relay_file[cpu]);
+ return -1;
+ }
+ }
+
+ total_bufsize = subbuf_size * n_subbufs;
+ relay_buffer[cpu] = mmap(NULL, total_bufsize, PROT_READ,
+ MAP_PRIVATE | MAP_POPULATE, relay_file[cpu],
+ 0);
+ if(relay_buffer[cpu] == MAP_FAILED)
+ {
+ printf("Couldn't mmap relay file, total_bufsize (%d) = subbuf_size (%d) * n_subbufs(%d), error = %s \n", (int)total_bufsize, (int)subbuf_size, (int)n_subbufs, strerror(errno));
+ close(relay_file[cpu]);
+ if (!print_only) close(out_file[cpu]);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * _init_relay_app - initialize the relay-app with specific netlink unit
+ * @relay_filebase: full path of base name of the per-cpu relayfs files
+ * @out_filebase: base name of the per-cpu files data will be written to
+ * @sub_buf_size: relayfs sub-buffer size of channel to be created
+ * @n_sub_bufs: relayfs number of sub-buffers of channel to be created
+ * @print_summary: boolean, print summary or not at end of run
+ * @netlink_unit: netlink unit, see netlink.h
+ *
+ * Returns 0 on success, negative otherwise.
+ *
+ * NOTE: use init_relay_app() instead if you don't need to specify a
+ * non-default netlink unit
+ */
+int _init_relay_app(const char *relay_filebase,
+ const char *out_filebase,
+ unsigned sub_buf_size,
+ unsigned n_sub_bufs,
+ int print_summary,
+ int netlink_unit)
+{
+ int i;
+ struct channel_create_info create_info;
+
+ ncpus = sysconf(_SC_NPROCESSORS_ONLN);
+ subbuf_size = sub_buf_size;
+ n_subbufs = n_sub_bufs;
+ print_totals = print_summary;
+ nl_unit = netlink_unit;
+
+ control_channel = open_control_channel();
+ if (control_channel < 0)
+ return -1;
+
+ create_info.subbuf_size = subbuf_size;
+ create_info.n_subbufs = n_subbufs;
+
+ send_request(RELAY_APP_STOP, NULL, 0); /* in case we exited badly before */
+ send_request(RELAY_APP_CHAN_CREATE, &create_info, sizeof(create_info));
+
+ for (i = 0; i < ncpus; i++) {
+ if (open_files(i, relay_filebase, out_filebase) < 0) {
+ printf("Couldn't open files\n");
+ send_request(RELAY_APP_STOP, NULL, 0);
+ send_request(RELAY_APP_CHAN_DESTROY, NULL, 0);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * init_relay_app - initialize the relay-app application
+ * @relay_filebase: full path of base name of the per-cpu relayfs files
+ * @out_filebase: base name of the per-cpu files data will be written to
+ * @sub_buf_size: relayfs sub-buffer size of channel to be created
+ * @n_sub_bufs: relayfs number of sub-buffers of channel to be created
+ * @print_summary: boolean, print summary or not at end of run
+ *
+ * Returns 0 on success, negative otherwise.
+ *
+ * The relayfs channel is created as a result of this function.
+ */
+int init_relay_app(const char *relay_filebase,
+ const char *out_filebase,
+ unsigned sub_buf_size,
+ unsigned n_sub_bufs,
+ int print_summary)
+{
+ return _init_relay_app(relay_filebase,
+ out_filebase,
+ sub_buf_size,
+ n_sub_bufs,
+ print_summary,
+ NETLINK_USERSOCK);
+}
+
+/**
+ * relay_app_main_loop - loop forever reading data
+ */
+int relay_app_main_loop(void)
+{
+ pthread_t thread;
+ int cpu, nb;
+ long i;
+ struct app_msg *msg;
+ unsigned short *ptr;
+ char tmpbuf[128];
+
+ signal(SIGINT, sigproc);
+ signal(SIGTERM, sigproc);
+ signal(SIGALRM, sigalarm);
+
+ send_request(RELAY_APP_START, NULL, 0);
+
+ for (i = 0; i < ncpus; i++) {
+ /* create a thread for each per-cpu buffer */
+ if (pthread_create(&thread, NULL, reader_thread, (void *)i) < 0) {
+ printf("Couldn't create thread\n");
+ send_request(RELAY_APP_STOP, NULL, 0);
+ send_request(RELAY_APP_CHAN_DESTROY, NULL, 0);
+ return -1;
+ }
+ }
+
+ logging = 1;
+
+ while (1) { /* handle messages from control channel */
+ nb = recv(control_channel, recvbuf, sizeof(recvbuf), 0);
+ struct nlmsghdr *nlh = (struct nlmsghdr *)recvbuf;
+
+ if (nb < 0) {
+ if (errno == EINTR)
+ continue;
+ printf("recv() failed\n");
+ } else if (nb == 0)
+ printf("unexpected EOF on netlink socket\n");
+ if (!NLMSG_OK(nlh, nb)) {
+ printf("netlink message not ok, nb = %d\n", nb);
+ continue;
+ }
+ switch (nlh->nlmsg_type) {
+ case RELAY_APP_BUF_INFO:
+ case RELAY_APP_SUBBUFS_CONSUMED:
+ msg = (struct app_msg *)nlh;
+ cpu = msg->info.cpu;
+ memcpy(&status[cpu].info, &msg->info, sizeof (struct buf_info));
+ pthread_mutex_lock(&status[cpu].ready_mutex);
+ if (status[cpu].info.produced > status[cpu].info.consumed)
+ pthread_cond_signal(&status[cpu].ready_cond);
+ pthread_mutex_unlock(&status[cpu].ready_mutex);
+ break;
+ case STP_REALTIME_DATA:
+ ptr = NLMSG_DATA(nlh);
+ fputs ( color[5], stdout);
+ fputs ((char *)ptr, stdout);
+ break;
+ case STP_EXIT:
+ /* module asks us to unload it and exit */
+ ptr = NLMSG_DATA(nlh);
+ /* FIXME. overflow check */
+ strcpy (tmpbuf, "/sbin/rmmod ");
+ strcpy (tmpbuf + strlen(tmpbuf), (char *)ptr);
+ printf ("Executing \"system %s\"\n", tmpbuf);
+ system (tmpbuf);
+ break;
+ case STP_DONE:
+ if (print_totals)
+ summarize();
+ close_all_files();
+ exit(0);
+ break;
+ default:
+ fprintf(stderr, "WARNING: ignored netlink message of type %d\n", (nlh->nlmsg_type));
+ }
+ }
+ return 0;
+}
diff --git a/runtime/stpd/librelay.h b/runtime/stpd/librelay.h
new file mode 100644
index 00000000..78acac47
--- /dev/null
+++ b/runtime/stpd/librelay.h
@@ -0,0 +1,51 @@
+/*
+ * librelay.h - relay-app user space 'library' header
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2005 - Tom Zanussi (zanussi@us.ibm.com), IBM Corp
+ *
+ */
+
+/* relay-app control channel command values */
+enum
+{
+ RELAY_APP_BUF_INFO = 1,
+ RELAY_APP_SUBBUFS_CONSUMED,
+ RELAY_APP_START,
+ RELAY_APP_STOP,
+ RELAY_APP_CHAN_CREATE,
+ RELAY_APP_CHAN_DESTROY,
+ RELAY_APP_USERCMD_START = 32
+};
+
+/*
+ * relay-app external API functions
+ */
+extern int init_relay_app(const char *relay_filebase,
+ const char *out_filebase,
+ unsigned sub_buf_size,
+ unsigned n_sub_bufs,
+ int print_summary);
+
+extern int _init_relay_app(const char *relay_filebase,
+ const char *out_filebase,
+ unsigned sub_buf_size,
+ unsigned n_sub_bufs,
+ int print_summary,
+ int netlink_unit);
+
+extern int relay_app_main_loop(void);
+extern int send_request(int type, void *data, int len);
diff --git a/runtime/stpd/stpd.c b/runtime/stpd/stpd.c
new file mode 100644
index 00000000..264cb36d
--- /dev/null
+++ b/runtime/stpd/stpd.c
@@ -0,0 +1,98 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "librelay.h"
+
+ /* relayfs base file name */
+static char *stpd_filebase = "/mnt/relay/stpd/cpu";
+
+ /* packet logging output written here, filebase0...N */
+static char *stpd_outfilebase = "stpd_cpu";
+
+#define DEFAULT_SUBBUF_SIZE (262144)
+#define DEFAULT_N_SUBBUFS (4)
+static unsigned subbuf_size = DEFAULT_SUBBUF_SIZE;
+static unsigned n_subbufs = DEFAULT_N_SUBBUFS;
+
+extern char *optarg;
+extern int optopt;
+int print_only = 0;
+int quiet = 0;
+
+static void usage(char *prog)
+{
+ fprintf(stderr, "%s [-p] [-q] [-b subbuf_size -n n_subbufs]\n", prog);
+ fprintf(stderr, "-p Print only. Don't log to files.\n");
+ fprintf(stderr, "-q Quiet. Don't display trace to stdout.\n");
+ fprintf(stderr, "-b subbuf_size (default is %d)\n", DEFAULT_SUBBUF_SIZE);
+ fprintf(stderr, "-b subbufs (default is %d)\n", DEFAULT_N_SUBBUFS);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ int c;
+ unsigned opt_subbuf_size = 0;
+ unsigned opt_n_subbufs = 0;
+
+ while ((c = getopt(argc, argv, "b:n:pq")) != EOF)
+ {
+ switch (c) {
+ case 'b':
+ opt_subbuf_size = (unsigned)atoi(optarg);
+ if (!opt_subbuf_size)
+ usage(argv[0]);
+ break;
+ case 'n':
+ opt_n_subbufs = (unsigned)atoi(optarg);
+ if (!opt_n_subbufs)
+ usage(argv[0]);
+ break;
+ case 'p':
+ print_only = 1;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ default:
+ usage(argv[0]);
+ }
+ }
+
+ if ( print_only && quiet)
+ {
+ fprintf (stderr, "Cannot do \"-p\" and \"-q\" both.\n");
+ usage(argv[0]);
+ }
+
+ if ((opt_n_subbufs && !opt_subbuf_size) ||
+ (!opt_n_subbufs && opt_subbuf_size))
+ usage(argv[0]);
+
+ if (opt_n_subbufs && opt_n_subbufs) {
+ subbuf_size = opt_subbuf_size;
+ n_subbufs = opt_n_subbufs;
+ }
+
+ if (init_relay_app(stpd_filebase, stpd_outfilebase,
+ subbuf_size, n_subbufs, 1))
+ {
+ fprintf(stderr, "Couldn't initialize relay app. Exiting.\n");
+ exit(1);
+ }
+
+ printf("Creating channel with %u sub-buffers of size %u.\n",
+ n_subbufs, subbuf_size);
+
+ if (quiet)
+ printf("Logging... Press Control-C to stop.\n");
+ else
+ printf("Press Control-C to stop.\n");
+
+ if (relay_app_main_loop()) {
+ printf("Couldn't enter main loop. Exiting.\n");
+ exit(1);
+ }
+
+ return 0;
+}
diff --git a/runtime/string.c b/runtime/string.c
new file mode 100644
index 00000000..a44cabc1
--- /dev/null
+++ b/runtime/string.c
@@ -0,0 +1,130 @@
+#ifndef _STRING_C_ /* -*- linux-c -*- */
+#define _STRING_C_
+
+#include <linux/config.h>
+
+/** @file string.c
+ * @addtogroup scbuf Scratch Buffer
+ * Scratch Buffer Functions.
+ * The scratch buffer is for collecting output before storing in a map,
+ * printing, etc. This is a per-cpu static buffer. It is necessary because
+ * of the limited stack space available in the kernel.
+ * @todo Need careful review of these to insure safety.
+ * @{
+ */
+
+#ifndef STP_STRING_SIZE
+#define STP_STRING_SIZE 2048
+#endif
+
+struct string {
+ short len;
+ short global;
+ char buf[STP_STRING_SIZE];
+};
+
+static struct string _stp_string[STP_NUM_STRINGS][NR_CPUS];
+
+typedef struct string *String;
+
+String _stp_string_init (int num)
+{
+ int global = 0;
+ String str;
+
+ if (num < 0) {
+ num = -num;
+ global = 1;
+ }
+
+ if (num >= STP_NUM_STRINGS) {
+ _stp_log ("_stp_string_init internal error: requested string exceeded allocated number");
+ return NULL;
+ }
+
+ if (global)
+ str = &_stp_string[num][0];
+ else
+ str = &_stp_string[num][smp_processor_id()];
+
+ str->global = global;
+ str->len = 0;
+ return str;
+}
+
+
+/** Sprint into the scratch buffer.
+ * Like printf, except output goes into a global scratch buffer
+ * which will contain the null-terminated output.
+ * Safe because overflowing the buffer is not allowed.
+ * Size is limited by length of scratch buffer, STP_BUF_LEN.
+ *
+ * @param fmt A printf-style format string followed by a
+ * variable number of args.
+ * @sa _stp_scbuf_clear
+ */
+
+void _stp_sprintf (String str, const char *fmt, ...)
+{
+ int num;
+ va_list args;
+ va_start(args, fmt);
+ num = vscnprintf(str->buf + str->len, STP_STRING_SIZE - str->len - 1, fmt, args);
+ va_end(args);
+ if (num > 0)
+ str->len += num;
+}
+
+void _stp_vsprintf (String str, const char *fmt, va_list args)
+{
+ int num;
+ num = vscnprintf(str->buf + str->len, STP_STRING_SIZE - str->len - 1, fmt, args);
+ if (num > 0)
+ str->len += num;
+}
+
+/** Write a string into the scratch buffer.
+ * Copies a string into a global scratch buffer.
+ * Safe because overflowing the buffer is not allowed.
+ * Size is limited by length of scratch buffer, STP_BUF_LEN.
+ * This is more efficient than using _stp_sprint().
+ *
+ * @param str A string.
+ */
+
+void _stp_string_cat_cstr (String str, const char *newstr)
+{
+ int num = strlen (newstr);
+ if (num > STP_STRING_SIZE - str->len - 1)
+ num = STP_STRING_SIZE - str->len - 1;
+ strncpy (str->buf + str->len, newstr, num+1);
+ str->len += num;
+}
+
+void _stp_string_cat_string (String str1, String str2)
+{
+ int num = str2->len;
+ if (num > STP_STRING_SIZE - str1->len - 1)
+ num = STP_STRING_SIZE - str1->len - 1;
+ strncpy (str1->buf + str1->len, str2->buf, num);
+ str1->len += num;
+}
+
+char * _stp_string_ptr (String str)
+{
+ return str->buf;
+}
+
+#define _stp_string_cat(str1, str2) \
+ ({ \
+ if (__builtin_types_compatible_p (typeof (str2), char[])) { \
+ char *x = (char *)str2; \
+ _str_string_cat_cstr(str1,x); \
+ } else { \
+ String x = (String)str2; \
+ _str_string_cat_string(str1,x); \
+ } \
+ })
+
+/** @} */
+#endif /* _STRING_C_ */
diff --git a/runtime/sym.c b/runtime/sym.c
index 523495f2..85662954 100644
--- a/runtime/sym.c
+++ b/runtime/sym.c
@@ -1,7 +1,7 @@
#ifndef _SYM_C_ /* -*- linux-c -*- */
#define _SYM_C_
-#include "scbuf.c"
+#include "string.c"
/** @file sym.c
* @addtogroup sym Symbolic Functions
@@ -19,21 +19,6 @@ static const char * (*_stp_kallsyms_lookup)(unsigned long addr,
unsigned long *offset,
char **modname, char *namebuf)=(void *)KALLSYMS_LOOKUP;
-static void __stp_symbol_print (unsigned long address, void (*prtfunc)(const char *fmt, ...))
-{
- char *modname;
- const char *name;
- unsigned long offset, size;
- char namebuf[KSYM_NAME_LEN+1];
-
- name = _stp_kallsyms_lookup(address, &size, &offset, &modname, namebuf);
-
- (*prtfunc)("0x%lx : ", address);
- if (modname)
- (*prtfunc)("%s+%#lx/%#lx [%s]", name, offset, size, modname);
- else
- (*prtfunc)("%s+%#lx/%#lx", name, offset, size);
-}
/** Print addresses symbolically into a string
* @param address The address to lookup.
@@ -42,11 +27,21 @@ static void __stp_symbol_print (unsigned long address, void (*prtfunc)(const cha
* @note Uses scbuf.
*/
-char * _stp_symbol_sprint (unsigned long address)
+String _stp_symbol_sprint (String str, unsigned long address)
{
- char *ptr = _stp_scbuf_cur();
- __stp_symbol_print (address, _stp_sprint);
- return ptr;
+ char *modname;
+ const char *name;
+ unsigned long offset, size;
+ char namebuf[KSYM_NAME_LEN+1];
+
+ name = _stp_kallsyms_lookup(address, &size, &offset, &modname, namebuf);
+
+ _stp_sprintf (str, "0x%lx : ", address);
+ if (modname)
+ _stp_sprintf (str, "%s+%#lx/%#lx [%s]", name, offset, size, modname);
+ else
+ _stp_sprintf (str, "%s+%#lx/%#lx", name, offset, size);
+ return str;
}
@@ -58,7 +53,18 @@ char * _stp_symbol_sprint (unsigned long address)
void _stp_symbol_print (unsigned long address)
{
- __stp_symbol_print (address, _stp_print);
+ char *modname;
+ const char *name;
+ unsigned long offset, size;
+ char namebuf[KSYM_NAME_LEN+1];
+
+ name = _stp_kallsyms_lookup(address, &size, &offset, &modname, namebuf);
+
+ _stp_printf ("0x%lx : ", address);
+ if (modname)
+ _stp_printf ("%s+%#lx/%#lx [%s]", name, offset, size, modname);
+ else
+ _stp_printf ("%s+%#lx/%#lx", name, offset, size);
}
/** @} */