Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add path builtin function #1492

Merged
merged 8 commits into from
Oct 14, 2020
Merged

Add path builtin function #1492

merged 8 commits into from
Oct 14, 2020

Conversation

olsajiri
Copy link
Contributor

Adding dpath builtin function that returns full path
referenced by struct path pointer in argument.

dpath(struct path *path)

Example:

  # bpftrace  -e 'kfunc:filp_close { printf("%s\n", dpath(args->filp->f_path)); }'
  Attaching 1 probe...
  /proc/sys/net/ipv6/conf/eno2/disable_ipv6
  /proc/sys/net/ipv6/conf/eno2/use_tempaddr
  socket:[23276]
  /proc/sys/net/ipv6/conf/eno2/disable_ipv6
  socket:[17655]
  /sys/devices/pci0000:00/0000:00:1c.5/0000:04:00.1/net/eno2/type
  socket:[38745]
  /proc/sys/net/ipv6/conf/eno2/disable_ipv6

The dpath returns the string with the path or empty string
on error. I don't think it's necessary to return error code
at the moment, however it can be added later in the second
optional argument.

if (has_d_path_.has_value())
return *has_d_path_;

struct bpf_insn insns[] = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this need a custom program? doesn't the helper detection work here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aah, I try to load the working program and check that try_load succeeds,
but I did not noticed that the detection stuff checks for 'invalid' string,
so the load can fail for other reasons but still detect the helper.. that might
actually work.. I'll try it ;-)

thanks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, the tracing program fails earlier with:
Tracing programs must provide btf_id

so we need to have special function for that

@fbs
Copy link
Contributor

fbs commented Aug 31, 2020

Nice! Glad this helper finally landed

@danobi
Copy link
Member

danobi commented Sep 1, 2020

Couple things:

  • are we locked into "dpath" name? I feel like "path" makes more sense here
  • last I heard bpf_d_path() works for whitelisted functions in the kernel. is that still the case? if so, I think it'd be important to return error code

@wilsonwr
Copy link

wilsonwr commented Sep 1, 2020

  • are we locked into "dpath" name? I feel like "path" makes more sense here

Personally, I appreciated it being called "dpath" when I saw this pull request. It helped me quickly understand that this function is like d_path() of the Linux kernel's filesystem API. Just my two cents.

@danobi
Copy link
Member

danobi commented Sep 1, 2020

  • are we locked into "dpath" name? I feel like "path" makes more sense here

Personally, I appreciated it being called "dpath" when I saw this pull request. It helped me quickly understand that this function is like d_path() of the Linux kernel's filesystem API. Just my two cents.

Yeah, I buy that. @olsajiri also made a good point in the IRC that fd_path() might still come one day.

@brendangregg
Copy link
Contributor

If possible, we shouldn't wed functions to Linux internals. There's already interest to see bpftrace on BSD and other OSes.

For example, I think an fdpath() function makes sense across kernels, since they all have a notion of an FD (POSIX).

This is trickier since it's for Linux struct dentry. I guess options are:

A) We go with dpath() and the docs say "this is a Linux specific function." BSD might add a vpath() to its port, etc.

B) We go with path() and the docs say "On Linux, this turns a dentry to a path; on BSD, this tuns a vnode to a path." etc.

C) The docs say "dpath() is an internal function that you should not use directly," and then we add a fdpath() wrapper that maps to code for doing the FD to dentry and then dpath() call (I'm sure I have examples of FD->dentry in the BPF book). Then we document fdpath() and it should work once other OSes have their own implementation.

D) We turn this PR into fdpath() with the FD->dentry code built in. :)

(C/D) Solves the problem of forking scripts and documentation over something trivial: path lookups, which will be used by a bunch of one-liners. It would be nice if basic scripts/documentation didn't fork so quickly (it will be necessary for deeper things of course).

Just thinking of a one-liner one might see in a hello-world-level tutorial:

# bpftrace -e 't:syscalls:sys_enter_read { @paths[fdpath(args->fd)] = count(); }'

(probe types [t for tracepoint] and their arguments are of course currently Linux-influenced; although if other OSes just use the abbreviations it mostly works: k: for kernel dynamic instrumentation, u: for user-level dynamic instrumentation, U: for user-level static instrumentation, and t: for...hang on, perhaps we should add a K: alternate alias for for kernel static instrumentation, and then we have an OS-agnostic set!).

@olsajiri what ended up in the whitelist? syscall tracepoints, ...?

@olsajiri
Copy link
Contributor Author

olsajiri commented Sep 2, 2020

If possible, we shouldn't wed functions to Linux internals. There's already interest to see bpftrace on BSD and other OSes.

For example, I think an fdpath() function makes sense across kernels, since they all have a notion of an FD (POSIX).

This is trickier since it's for Linux struct dentry. I guess options are:

A) We go with dpath() and the docs say "this is a Linux specific function." BSD might add a vpath() to its port, etc.

B) We go with path() and the docs say "On Linux, this turns a dentry to a path; on BSD, this tuns a vnode to a path." etc.

hm, for the sake of scripts I think maybe we should use just 'path'

we will overload path's argument to be different type,
but it'd be for different systems, so they would not collide
at the same time

C) The docs say "dpath() is an internal function that you should not use directly," and then we add a fdpath() wrapper that maps to code for doing the FD to dentry and then dpath() call (I'm sure I have examples of FD->dentry in the BPF book). Then we document fdpath() and it should work once other OSes have their own implementation.

D) We turn this PR into fdpath() with the FD->dentry code built in. :)

I might have confused things when mentioning dentry.. it was the answer for
where's the 'd' in d_path coming from.. but the dpath function argument
is actually 'struct path' object, which is also argument for kernel's d_path
function

(C/D) Solves the problem of forking scripts and documentation over something trivial: path lookups, which will be used by a bunch of one-liners. It would be nice if basic scripts/documentation didn't fork so quickly (it will be necessary for deeper things of course).

Just thinking of a one-liner one might see in a hello-world-level tutorial:

# bpftrace -e 't:syscalls:sys_enter_read { @paths[fdpath(args->fd)] = count(); }'

(probe types [t for tracepoint] and their arguments are of course currently Linux-influenced; although if other OSes just use the abbreviations it mostly works: k: for kernel dynamic instrumentation, u: for user-level dynamic instrumentation, U: for user-level static instrumentation, and t: for...hang on, perhaps we should add a K: alternate alias for for kernel static instrumentation, and then we have an OS-agnostic set!).

@olsajiri what ended up in the whitelist? syscall tracepoints, ...?

vfs_truncate
vfs_fallocate
dentry_open
vfs_getattr
filp_close

not much ;-) I recall we discussed that at some point you could provide list
of functions you need on whitelist and fs folks would review if that's ok

the kernel change is just matter of adding line like:

diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index b2a5380eb187..4cff2f891d28 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -1123,6 +1123,7 @@ BTF_ID(func, vfs_fallocate)
 BTF_ID(func, dentry_open)
 BTF_ID(func, vfs_getattr)
 BTF_ID(func, filp_close)
+BTF_ID(func, function)
 BTF_SET_END(btf_allowlist_d_path)
 
 static bool bpf_d_path_allowed(const struct bpf_prog *prog)

I think we could add support for tracepoints as well,
but I'd like to add raw tracepoint support first to bpftrace,
which is tricky ATM and needs some pahole changes

@javierhonduco
Copy link
Contributor

This is very exciting! 😄 cc @tyroguru

@olsajiri
Copy link
Contributor Author

The latest version renames 'dpath' to 'path', could you please check? thanks @fbs @danobi @brendangregg

@fbs
Copy link
Contributor

fbs commented Sep 25, 2020

Looks ok 👍

Can you add a runtime test for this? By doing something like 5441336 you can limit the tests to systems that support it:

NAME openat uptr
RUN bpftrace -v -e 't:syscalls:sys_enter_openat /comm == "syscall"/ { print(str(uptr(args->filename))) }' -c "./testprogs/syscall openat"
EXPECT bpftrace_runtime_test_syscall
REQUIRES_FEATURE probe_read_kernel
TIMEOUT 5

@olsajiri
Copy link
Contributor Author

Looks ok +1

Can you add a runtime test for this? By doing something like 5441336 you can limit the tests to systems that support it:

NAME openat uptr
RUN bpftrace -v -e 't:syscalls:sys_enter_openat /comm == "syscall"/ { print(str(uptr(args->filename))) }' -c "./testprogs/syscall openat"
EXPECT bpftrace_runtime_test_syscall
REQUIRES_FEATURE probe_read_kernel
TIMEOUT 5

ah, did not see REQUIRES_FEATURE, nice.. will add

@olsajiri olsajiri changed the title Add dpath builtin function Add path builtin function Sep 26, 2020
@olsajiri olsajiri requested a review from fbs September 29, 2020 07:14
@fbs
Copy link
Contributor

fbs commented Oct 2, 2020

@brendangregg @mmisono @danobi . Code looks ok (but I don't have a good btf test system atm). Any thoughts on this one?

@mmisono
Copy link
Collaborator

mmisono commented Oct 2, 2020

BPF_FUNC_d_path is not merged in mainline yet, is it?

bpftrace_test does not run w/o root.

/home/ubuntu/work/bpftrace/bpftrace/tests/semantic_analyser.cpp:70: Failure
Expected equality of these values:
  fields.analyse()
    Which is: 1
  expected_field_analyser
    Which is: 0

Input:
kfunc:filp_close { $k = path( args->filp->f_path ) }

Output:
stdin:1:1-17: ERROR: kfunc:filp_close: could not read traceable functions from /sys/kernel/debug/tracing/available_filter_functions (is debugfs mounted?)
kfunc:filp_close { $k = path( args->filp->f_path ) }
~~~~~~~~~~~~~~~~

/home/ubuntu/work/bpftrace/bpftrace/tests/semantic_analyser.cpp:70: Failure
Expected equality of these values:
  fields.analyse()
    Which is: 1
  expected_field_analyser
    Which is: 0

Input:
kretfunc:fget { $k = path( retval->f_path ) }

Output:
stdin:1:1-14: ERROR: kfunc:fget: could not read traceable functions from /sys/kernel/debug/tracing/available_filter_functions (is debugfs mounted?)
kretfunc:fget { $k = path( retval->f_path ) }
~~~~~~~~~~~~~

[  FAILED  ] semantic_analyser.call_path (788 ms)

@mmisono
Copy link
Collaborator

mmisono commented Oct 2, 2020

@olsajiri
Could you include the error checking code? mmisono@bddb3c2
(I assume that dpath return <0 if error)

@olsajiri
Copy link
Contributor Author

olsajiri commented Oct 2, 2020

@olsajiri
Could you include the error checking code? mmisono@bddb3c2
(I assume that dpath return <0 if error)

sure, will fix that, thnx

@@ -791,6 +791,24 @@ TEST(semantic_analyser, call_stack)
test("kprobe:f { @x = 3; ustack(perf, @x) }", 10);
}

TEST(semantic_analyser, call_path)
{
BPFfeature feature;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
BPFfeature feature;

MockBPFFeature handles this part. Allows us to run tests without root.

@olsajiri
Copy link
Contributor Author

olsajiri commented Oct 4, 2020

@olsajiri
Could you include the error checking code? mmisono@bddb3c2
(I assume that dpath return <0 if error)

sure, will fix that, thnx

I checked and we don't check for error in there, it returns void,
but I should have used createCall call instead

@mmisono
Copy link
Collaborator

mmisono commented Oct 5, 2020

@olsajiri
Could you include the error checking code? mmisono@bddb3c2
(I assume that dpath return <0 if error)

sure, will fix that, thnx

I checked and we don't check for error in there, it returns void,
but I should have used createCall call instead

The commit is for reporting helper errors if any (the functionality introduced in #1276.) Or do you mean bpf_d_path() helper returns void?

@olsajiri
Copy link
Contributor Author

olsajiri commented Oct 5, 2020

@olsajiri
Could you include the error checking code? mmisono@bddb3c2
(I assume that dpath return <0 if error)

sure, will fix that, thnx

I checked and we don't check for error in there, it returns void,
but I should have used createCall call instead

The commit is for reporting helper errors if any (the functionality introduced in #1276.) Or do you mean bpf_d_path() helper returns void?

ah I misunderstood, sry.. yes, it's good change, I'll add it, thanks

Comment on lines 2706 to 2722
## 23. `path()`: Returns full path referenced by struct path pointer in argument

Syntax: `path(struct path *path)`
# bpftrace -e 'kfunc:filp_close { printf("%s\n", path(args->filp->f_path)); }'
Attaching 1 probe...
/proc/sys/net/ipv6/conf/eno2/disable_ipv6
/proc/sys/net/ipv6/conf/eno2/use_tempaddr
socket:[23276]
/proc/sys/net/ipv6/conf/eno2/disable_ipv6
socket:[17655]
/sys/devices/pci0000:00/0000:00:1c.5/0000:04:00.1/net/eno2/type
socket:[38745]
/proc/sys/net/ipv6/conf/eno2/disable_ipv6

# bpftrace -e 'kretfunc:dentry_open { printf("%s\n", path(retval->f_path)); }'
Attaching 1 probe...
/dev/pts/1 -> /dev/pts/1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ends up in the example codeblock of sizeof(). Also this should probably go at the end as number 25 (instead of reusing number 23)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, I reformatted it, thanks

@fbs
Copy link
Contributor

fbs commented Oct 9, 2020

Can you rebase this on master? The addrspace stuff just landed.

Syntax:
- `path(struct path *path)`

Return full path referenced by struct path pointer in argument.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more thing: maybe it'd be good to document there's a whilelist of kernel functions you can use this on. Could be confusing for users who get load errors when they have a correct handle to struct path.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, I added a sentence about that.. I think we need some way to get the whitelist info from kernel, I'll try to add something

olsajiri and others added 8 commits October 12, 2020 11:20
When processing pointer attach point arguments we also need
to add pointer record to the BTF set, se we are able to
dereference it later on. This will allow to resolve f_path
member in dpath call (added in following patches):

  # bpftrace  -e 'kfunc:filp_close { printf("%s\n", dpath(args->filp->f_path)); }'

Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Adding fail argument to check_arg so we can have it failed,
but still give a chance for other type. It will be used in
following patch for d_path check, that allows both pointer
and record types in the argument.

Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Adding BPFfeature::has_d_path function to test
if dpath helper is available.

Display it for --info option:

  # bpftrace --info
  ...
  Kernel helpers
    ...
    get_current_cgroup_id: yes
    send_signal: yes
    override_return: no
    dpath: yes

Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Adding path builtin function that returns full path
referenced by struct path pointer in argument.

  path(struct path *path)

Example:

  # bpftrace  -e 'kfunc:filp_close { printf("%s\n", path(args->filp->f_path)); }'
  Attaching 1 probe...
  /proc/sys/net/ipv6/conf/eno2/disable_ipv6
  /proc/sys/net/ipv6/conf/eno2/use_tempaddr
  socket:[23276]
  /proc/sys/net/ipv6/conf/eno2/disable_ipv6
  socket:[17655]
  /sys/devices/pci0000:00/0000:00:1c.5/0000:04:00.1/net/eno2/type
  socket:[38745]
  /proc/sys/net/ipv6/conf/eno2/disable_ipv6

The path returns the string with the path or empty string
on error. I don't think it's necessary to return error code
at the moment, however it can be added later in the second
optional argument.

Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Adding semantic_analyser test.

Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Adding path runtime test that checks on close being called
on file within './testprogs/syscall read' test prog.

Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Signed-off-by: Masanori Misono <m.misono760@gmail.com>
@fbs fbs merged commit 688cab9 into bpftrace:master Oct 14, 2020
@ajor ajor mentioned this pull request Oct 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants