diff options
| author | Frank Ch. Eigler <fche@redhat.com> | 2025-09-15 13:02:23 -0400 |
|---|---|---|
| committer | Frank Ch. Eigler <fche@elastic.org> | 2025-09-15 13:07:36 -0400 |
| commit | 1fe4178def008f562244e1820d0ffe10f774e78a (patch) | |
| tree | e20a9c6259e5fdf30482e1ad03303242f0b04cf6 | |
| parent | systemtap.syscall/*.exp: [file tail FOO] in pass/fail messages (diff) | |
PR33428: imply kernel<vmlinux.h> in kernel-side @cast() for type info
Linux 5.7+ includes a vmlinux.h file generated during build that includes most or all kernel-side data structure declarations, mainly for use by BPF compilers. By processing that into DWARF via the typequery mechanism, we can generate kernel data structure debuginfo for use in many @cast operations, without having to painstakingly enumerate and track the exact kernel header files containing those declarations. This search is active and preferred by default for all kernel-space @cast operations. It can be disabled with "--compatible=5.3" .
| -rw-r--r-- | NEWS | 8 | ||||
| -rw-r--r-- | buildrun.cxx | 29 | ||||
| -rw-r--r-- | man/stap.1.in | 4 | ||||
| -rw-r--r-- | tapsets.cxx | 40 | ||||
| -rw-r--r-- | testsuite/semok/pr33428.stp | 8 |
5 files changed, 76 insertions, 13 deletions
| @@ -1,5 +1,13 @@ | |||
| 1 | * What's new in version 5.4 | 1 | * What's new in version 5.4 |
| 2 | 2 | ||
| 3 | - The kernel-context @cast operator now implicitly searches a kernel's | ||
| 4 | <vmlinux.h> generated header file first, if available (kernel 5.7+), | ||
| 5 | for type declarations. This can make some debuginfo access | ||
| 6 | unnecessary, and thus processing faster. It can work around kernel | ||
| 7 | API changes where type declarations move between headers. | ||
| 8 | Use --compatible=5.3 to disable this behaviour. | ||
| 9 | Use @cast(..., "kernel<vmlinux.h>") manually if desired. | ||
| 10 | |||
| 3 | - Type checking and autocast processing have been made more thorough, | 11 | - Type checking and autocast processing have been made more thorough, |
| 4 | so elided variables are checked more and @defined() tests may be | 12 | so elided variables are checked more and @defined() tests may be |
| 5 | more complicated. Preexisting scripts that rely on elision for | 13 | more complicated. Preexisting scripts that rely on elision for |
diff --git a/buildrun.cxx b/buildrun.cxx index ad299d0fc..1f56a976b 100644 --- a/buildrun.cxx +++ b/buildrun.cxx | |||
| @@ -1201,9 +1201,19 @@ make_typequery_kmod(systemtap_session& s, const vector<string>& headers, string& | |||
| 1201 | // full kernel build tree, it's possible to get at types that aren't in the | 1201 | // full kernel build tree, it's possible to get at types that aren't in the |
| 1202 | // normal include path, e.g.: | 1202 | // normal include path, e.g.: |
| 1203 | // @cast(foo, "bsd_acct_struct", "kernel<kernel/acct.c>")->... | 1203 | // @cast(foo, "bsd_acct_struct", "kernel<kernel/acct.c>")->... |
| 1204 | omf << "CFLAGS_" << basename << ".o :="; | 1204 | omf << "CFLAGS_" << basename << ".o := -w "; // suppress warnings |
| 1205 | bool no_vmlinux_h = true; | ||
| 1205 | for (size_t i = 0; i < headers.size(); ++i) | 1206 | for (size_t i = 0; i < headers.size(); ++i) |
| 1206 | omf << " -include " << lex_cast_qstring(headers[i]); // XXX right quoting? | 1207 | { |
| 1208 | const string& h = headers[i]; | ||
| 1209 | if (h == string("vmlinux.h")) // PR33428: vmlinux.h special case | ||
| 1210 | { | ||
| 1211 | omf << " -include " << lex_cast_qstring(s.kernel_build_tree) << "/" << lex_cast_qstring("vmlinux.h"); | ||
| 1212 | no_vmlinux_h = false; | ||
| 1213 | } | ||
| 1214 | else | ||
| 1215 | omf << " -include " << lex_cast_qstring(h); // XXX right quoting? | ||
| 1216 | } | ||
| 1207 | omf << endl; | 1217 | omf << endl; |
| 1208 | 1218 | ||
| 1209 | omf << "obj-m := " + basename + ".o" << endl; | 1219 | omf << "obj-m := " + basename + ".o" << endl; |
| @@ -1213,10 +1223,17 @@ make_typequery_kmod(systemtap_session& s, const vector<string>& headers, string& | |||
| 1213 | string source(dir + "/" + basename + ".c"); | 1223 | string source(dir + "/" + basename + ".c"); |
| 1214 | ofstream osrc(source.c_str()); | 1224 | ofstream osrc(source.c_str()); |
| 1215 | 1225 | ||
| 1216 | // this is mandated by linux kbuild as of 5.11+ | 1226 | if (no_vmlinux_h) { |
| 1217 | osrc << "#include <linux/module.h>" << endl; | 1227 | // this is mandated by linux kbuild as of 5.11+ |
| 1218 | osrc << "MODULE_LICENSE(\"GPL\");" << endl; | 1228 | osrc << "#include <linux/module.h>" << endl; |
| 1219 | osrc << "MODULE_DESCRIPTION(\"" << basename << "\");" << endl; | 1229 | osrc << "MODULE_LICENSE(\"GPL\");" << endl; |
| 1230 | osrc << "MODULE_DESCRIPTION(\"" << basename << "\");" << endl; | ||
| 1231 | } else { | ||
| 1232 | // PR33428: do the same as the above, but without using linux/module.h macro infrastructure, because | ||
| 1233 | // vmlinux.h conflicts with kernel headers. | ||
| 1234 | osrc << "static const char modinfo[] __attribute__((__used__)) __attribute__((__section__(\".modinfo\"))) = \"license=GPL\\0description=typequery\";" << endl; | ||
| 1235 | } | ||
| 1236 | |||
| 1220 | 1237 | ||
| 1221 | osrc.close(); | 1238 | osrc.close(); |
| 1222 | 1239 | ||
diff --git a/man/stap.1.in b/man/stap.1.in index 80c9d50d0..0a069eba7 100644 --- a/man/stap.1.in +++ b/man/stap.1.in | |||
| @@ -1899,6 +1899,10 @@ surrounded by angle brackets, in case normal debuginfo is not available. For | |||
| 1899 | kernel headers, prefix it with "kernel" to use the appropriate build system. | 1899 | kernel headers, prefix it with "kernel" to use the appropriate build system. |
| 1900 | All other headers are built with default GCC parameters into a user module. | 1900 | All other headers are built with default GCC parameters into a user module. |
| 1901 | Multiple headers may be specified in sequence to resolve a codependency. | 1901 | Multiple headers may be specified in sequence to resolve a codependency. |
| 1902 | .PP | ||
| 1903 | As of systemtap version 5.4, the kernel "<vmlinux.h>" header is tried before | ||
| 1904 | any specified kernel headers or modules, which may make it unnecessary to list | ||
| 1905 | any explicit module or header file names for kernel data structures. | ||
| 1902 | .SAMPLE | 1906 | .SAMPLE |
| 1903 | @cast(tv, "timeval", "<sys/time.h>")\->tv_sec | 1907 | @cast(tv, "timeval", "<sys/time.h>")\->tv_sec |
| 1904 | @cast(task, "task_struct", "kernel<linux/sched.h>")\->tgid | 1908 | @cast(task, "task_struct", "kernel<linux/sched.h>")\->tgid |
diff --git a/tapsets.cxx b/tapsets.cxx index 38c8a49b2..d267b6506 100644 --- a/tapsets.cxx +++ b/tapsets.cxx | |||
| @@ -500,10 +500,10 @@ static const string TOK_LIBRARY("library"); | |||
| 500 | static const string TOK_PLT("plt"); | 500 | static const string TOK_PLT("plt"); |
| 501 | static const string TOK_METHOD("method"); | 501 | static const string TOK_METHOD("method"); |
| 502 | static const string TOK_CLASS("class");; | 502 | static const string TOK_CLASS("class");; |
| 503 | static const string TOK_CALLEE("callee");; | 503 | static const string TOK_CALLEE("callee"); |
| 504 | static const string TOK_CALLEES("callees");; | 504 | static const string TOK_CALLEES("callees"); |
| 505 | static const string TOK_NEAREST("nearest");; | 505 | static const string TOK_NEAREST("nearest"); |
| 506 | 506 | static const string TOK_KERNEL_VMLINUX_H("kernel<vmlinux.h>"); | |
| 507 | 507 | ||
| 508 | 508 | ||
| 509 | struct dwarf_query; // forward decl | 509 | struct dwarf_query; // forward decl |
| @@ -4853,7 +4853,7 @@ dwarf_var_expanding_visitor::visit_target_symbol (target_symbol *e) | |||
| 4853 | void | 4853 | void |
| 4854 | dwarf_var_expanding_visitor::visit_cast_op (cast_op *e) | 4854 | dwarf_var_expanding_visitor::visit_cast_op (cast_op *e) |
| 4855 | { | 4855 | { |
| 4856 | // Fill in our current module context if needed | 4856 | // Fill in our current module context if needed, i.e., absent third field in @cast() |
| 4857 | if (e->module.empty()) | 4857 | if (e->module.empty()) |
| 4858 | { | 4858 | { |
| 4859 | // Backward compatibility for @cast() ops, sans module string, | 4859 | // Backward compatibility for @cast() ops, sans module string, |
| @@ -4862,7 +4862,16 @@ dwarf_var_expanding_visitor::visit_cast_op (cast_op *e) | |||
| 4862 | if (strverscmp(sess.compatible.c_str(), "4.3") < 0) | 4862 | if (strverscmp(sess.compatible.c_str(), "4.3") < 0) |
| 4863 | e->module = "kernel"; | 4863 | e->module = "kernel"; |
| 4864 | else | 4864 | else |
| 4865 | e->module = q.dw.module_name; | 4865 | { |
| 4866 | // absolute /user/space/path/name xor kernel xor kernel-module name | ||
| 4867 | if (is_user_module (q.dw.module_name)) | ||
| 4868 | e->module = q.dw.module_name; | ||
| 4869 | else if ((strverscmp(sess.compatible.c_str(), "5.4") >= 0) && // default on new enough systemtap | ||
| 4870 | (strverscmp(sess.kernel_base_release.c_str(), "6.7") >= 0)) // for new enough kernel to have a vmlinux.h | ||
| 4871 | e->module = string(TOK_KERNEL_VMLINUX_H) + string(":") + q.dw.module_name; // PR33428: prefix | ||
| 4872 | else | ||
| 4873 | e->module = q.dw.module_name; | ||
| 4874 | } | ||
| 4866 | } | 4875 | } |
| 4867 | 4876 | ||
| 4868 | var_expanding_visitor::visit_cast_op(e); | 4877 | var_expanding_visitor::visit_cast_op(e); |
| @@ -5153,10 +5162,27 @@ void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e) | |||
| 5153 | 5162 | ||
| 5154 | // split the module string by ':' for alternatives | 5163 | // split the module string by ':' for alternatives |
| 5155 | vector<string> modules; | 5164 | vector<string> modules; |
| 5165 | |||
| 5166 | // PR33428: prepend "kernel<vmlinux.h>" to the list if there is any | ||
| 5167 | // "kernel" or "kernel<FILE>" component. | ||
| 5168 | if ((strverscmp(sess.compatible.c_str(), "5.4") >= 0) && // default on new enough systemtap | ||
| 5169 | (strverscmp(sess.kernel_base_release.c_str(), "6.7") >= 0) && // for new enough kernel to have a vmlinux.h | ||
| 5170 | (e->module.find(TOK_KERNEL_VMLINUX_H) == string::npos)) // don't prepend again; might already be here from implicit "" expansion | ||
| 5171 | { | ||
| 5172 | if (e->module.starts_with("kernel")) // right at the front? | ||
| 5173 | e->module = string(TOK_KERNEL_VMLINUX_H) + string(":") + e->module; | ||
| 5174 | else { | ||
| 5175 | string::size_type p = e->module.find(":kernel"); // in the middle? | ||
| 5176 | if (p != string::npos) | ||
| 5177 | e->module.insert(p, TOK_KERNEL_VMLINUX_H + string (":")); | ||
| 5178 | } | ||
| 5179 | } | ||
| 5180 | |||
| 5156 | tokenize(e->module, modules, ":"); | 5181 | tokenize(e->module, modules, ":"); |
| 5157 | bool userspace_p=false; // PR10601 | 5182 | |
| 5158 | for (unsigned i = 0; !result && i < modules.size(); ++i) | 5183 | for (unsigned i = 0; !result && i < modules.size(); ++i) |
| 5159 | { | 5184 | { |
| 5185 | bool userspace_p=false; // PR10601 | ||
| 5160 | string& module = modules[i]; | 5186 | string& module = modules[i]; |
| 5161 | filter_special_modules(module); | 5187 | filter_special_modules(module); |
| 5162 | 5188 | ||
diff --git a/testsuite/semok/pr33428.stp b/testsuite/semok/pr33428.stp new file mode 100644 index 000000000..2557b00a3 --- /dev/null +++ b/testsuite/semok/pr33428.stp | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #! stap -p2 | ||
| 2 | |||
| 3 | probe module("nfsv3").function("nfs3_proc_access"), kernel.function("do_sys_open") | ||
| 4 | { | ||
| 5 | print(@cast(0xf00d, "struct mount")$, "kernel<vmlinux.h") // explicit | ||
| 6 | print(@cast(0xbeef, "struct mount")$) // implicit "nfsv3"->"kernel<vmlinux.h>:nfsv3" | ||
| 7 | print(@cast(0xdead, "struct mount")$,"kernel<linux/sched.h>") // explicit ->"kernel<vmlinux.h>:kernel<linux/sched.h>" | ||
| 8 | } | ||
