diff options
author | hunt <hunt> | 2005-04-07 15:17:29 +0000 |
---|---|---|
committer | hunt <hunt> | 2005-04-07 15:17:29 +0000 |
commit | 3d4bc8bea6b45893bd4b49f44df26bd602b4cba5 (patch) | |
tree | bae3fb1bc5fcbd7906574b6902c649b203274a3f | |
parent | 6e01db401be4a19e6d81d9955d055e2f4e1b6aea (diff) | |
download | systemtap-steved-3d4bc8bea6b45893bd4b49f44df26bd602b4cba5.tar.gz systemtap-steved-3d4bc8bea6b45893bd4b49f44df26bd602b4cba5.tar.xz systemtap-steved-3d4bc8bea6b45893bd4b49f44df26bd602b4cba5.zip |
Update to use relayfs.
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 </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 </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 </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 </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 </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 </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 </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 Page</a> | <a class="qindex" href="modules.html">Modules</a> | <a class="qindex" href="annotated.html">Data Structures</a> | <a class="qindex" href="dirs.html">Directories</a> | <a class="qindex" href="files.html">File List</a> | <a class="qindex" href="functions.html">Data Fields</a> | <a class="qindex" href="globals.html">Globals</a> | <a class="qindex" href="pages.html">Related Pages</a></div> <div class="nav"> <a class="el" href="dir_000000.html">probes</a> / <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 </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"> </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 </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 </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 </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"> </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 * </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"> </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 </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"> </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">( </td> <td class="md" nowrap valign="top">void </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> </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">( </td> + <td class="md" nowrap valign="top">const char * </td> + <td class="mdname1" valign="top" nowrap> <em>str</em> </td> + <td class="md" valign="top"> ) </td> + <td class="md" nowrap></td> + </tr> + </table> + </td> + </tr> +</table> +<table cellspacing="5" cellpadding="0" border="0"> + <tr> + <td> + + </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> </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 </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 </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"> </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 * </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">( </td> + <td class="md" nowrap valign="top">int </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 </td> + <td class="mdname" nowrap> <em>levels</em></td> + </tr> + <tr> + <td class="md"></td> + <td class="md">) </td> + <td class="md" colspan="2"></td> + </tr> + </table> + </td> + </tr> +</table> +<table cellspacing="5" cellpadding="0" border="0"> + <tr> + <td> + + </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> </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-><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-><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 Page</a> | <a class="qindex" href="modules.html">Modules</a> | <a class="qindex" href="annotated.html">Data Structures</a> | <a class="qindex" href="dirs.html">Directories</a> | <a class="qindex" href="files.html">File List</a> | <a class="qindex" href="functions.html">Data Fields</a> | <a class="qindex" href="globals.html">Globals</a> | <a class="qindex" href="pages.html">Related 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 > 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 > _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 <linux/config.h></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 > 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 > _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 (&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 (&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 (&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 (&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 </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 </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"> </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 * </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 <linux/module.h></span> 00005 <span class="preprocessor">#include <linux/interrupt.h></span> 00006 <span class="preprocessor">#include <net/sock.h></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 <hunt@redhat.com>"</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 <linux/netlink.h></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 <hunt@redhat.com>"</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->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->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->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->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 (&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->len; +00083 <span class="comment">// dlog ("skblen = %d %d\n", skblen, sizeof(*nlh));</span> +00084 <span class="keywordflow">if</span> (skblen < <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->data; +00088 nlmsglen = nlh->nlmsg_len; +00089 +00090 <span class="comment">// dlog ("nlmsghlen=%d\n", nlmsglen);</span> +00091 if (nlmsglen < sizeof(*nlh) || skblen < nlmsglen) +00092 return; +00093 +00094 pid = nlh->nlmsg_pid; +00095 flags = nlh->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 <= 0 || !(flags & 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->str); -00115 -00116 foreach (opens, ptr) -00117 dlog ("opens[%s] = %lld\n", key1str(ptr), ptr->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->stats.count, st->stats.sum, st->stats.min, st->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->stats.count, st->stats.sum, st->stats.min, st->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 & 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->nlmsg_type,pid);</span> +00111 +00112 <span class="keywordflow">if</span> (flags & 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(&sk->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->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->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->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 (&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->str); +00225 +00226 foreach (opens, ptr) +00227 nlog ("opens[%s] = %lld\n", key1str(ptr), ptr->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->stats.count, st->stats.sum, st->stats.min, st->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->stats.count, st->stats.sum, st->stats.min, st->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->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 Page</a> | <a class="qindex" href="modules.html">Modules</a> | <a class="qindex" href="annotated.html">Data Structures</a> | <a class="qindex" href="dirs.html">Directories</a> | <a class="qindex" href="files.html">File List</a> | <a class="qindex" href="functions.html">Data Fields</a> | <a class="qindex" href="globals.html">Globals</a> | <a class="qindex" href="pages.html">Related 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); } /** @} */ |