-
Notifications
You must be signed in to change notification settings - Fork 187
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
pkg/uprobetracer: use kfilefields to identify inodes #2669
Conversation
ReadFInodeFromFd
from kfilefields to identify inodesReadFInodeFromFd
from kfilefields to identify inodes
c86d00c
to
5ade22d
Compare
pkg/uprobetracer/tracer.go
Outdated
func getInodeUUID(file *os.File) (uint64, error) { | ||
token, err := kfilefields.ReadFInodeFromFd(int(file.Fd())) | ||
priv, err := kfilefields.ReadPrivateDataFromFd(int(file.Fd())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since getInodeUUID()
can be called several times for a single gadget, it would make sense to optimize kfilefields.ReadFInodeFromFd
to create the kfilefield ebpf program only once. Maybe with:
func ReadFInodesFromsFd(fd []int) ([]uint64, error)
(this could be done in a future PR)
ReadFInodeFromFd
from kfilefields to identify inodes
I use the following to test it: package main
import (
"fmt"
"os"
"github.com/inspektor-gadget/inspektor-gadget/pkg/kfilefields"
)
func main() {
for _, filename := range os.Args[1:] {
file, err := os.Open(filename)
if err != nil {
panic(err)
}
pd, err := kfilefields.ReadPrivateDataFromFd(int(file.Fd()))
if err != nil {
panic(err)
}
pi, err := kfilefields.ReadPrivateInodeFromFd(int(file.Fd()))
if err != nil {
panic(err)
}
fmt.Printf("%v\n", filename)
fmt.Printf("PrivateData: %v\nPrivateInode: %v\n", pd, pi)
runtime.KeepAlive(file)
}
} Build it:
Prepare 2 busybox containers:
Get the pids of the 2 containers:
A couple of tests on
The results are not consistent. So I don't think it works fine. |
pkg/kfilefields/bpf/filefields.bpf.c
Outdated
ff->private_data = (u64)BPF_CORE_READ(ret, private_data); | ||
ff->f_op = (u64)BPF_CORE_READ(ret, f_op); | ||
private_file = (struct file *)ff->private_data; | ||
ff->private_inode = (u64)BPF_CORE_READ(private_file, f_inode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(struct file *)->private_data
is casted to a struct file *
but this is only valid if the filesystem is overlay.
We can check if it is the case with (struct file *)->i_sb->s_type->name == "overlay"
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better check without string comparisons:
--- a/pkg/kfilefields/bpf/filefields.bpf.c
+++ b/pkg/kfilefields/bpf/filefields.bpf.c
@@ -4,6 +4,11 @@
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_tracing.h>
+// include/uapi/linux/magic.h
+#ifndef OVERLAYFS_SUPER_MAGIC
+#define OVERLAYFS_SUPER_MAGIC 0x794c7630
+#endif
+
// configured by userspace
const volatile u64 socket_ino = 0;
@@ -52,6 +57,7 @@ int BPF_KRETPROBE(ig_fget_x, struct file *ret)
u64 current_pid_tgid;
struct file_fields *ff;
struct file *private_file;
+ struct file *real_file;
int zero = 0;
if (tracer_pid_tgid == 0)
@@ -68,8 +74,12 @@ int BPF_KRETPROBE(ig_fget_x, struct file *ret)
ff->private_data = (u64)BPF_CORE_READ(ret, private_data);
ff->f_op = (u64)BPF_CORE_READ(ret, f_op);
- private_file = (struct file *)ff->private_data;
- ff->private_inode = (u64)BPF_CORE_READ(private_file, f_inode);
+
+ real_file = ret;
+ if (BPF_CORE_READ(ret, f_inode, i_sb, s_magic) == OVERLAYFS_SUPER_MAGIC) {
+ real_file = (struct file *) ff->private_data;
+ }
+ ff->private_inode = (u64)BPF_CORE_READ(real_file, f_inode);
// Initialize private_data for only one execution of __scm_send
tracer_pid_tgid = 0;
Yes, I can reproduce. At most of the time it works, but sometimes the |
The randomness was due to a bug in my test program main.go. I added |
OK, I've learned once again that if a Golang program encounters sporadic randomness issues, the first thing to check is the garbage collector. |
ca69d87
to
103eeab
Compare
It needs |
Done, thanks. |
pkg/uprobetracer/tracer.go
Outdated
// keeps the fd and refCount for each inodeUUID | ||
inodeRefCount map[inodeUUID]*inodeKeeper | ||
inodeRefCount map[uint64]*inodeKeeper |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest to rename inodeUUID
to realInodePtr
everywhere. What do you think?
And the comment before inodeRefCount
should briefly explain why the real inode ptr is used instead of just the inode number, and why we need to care about getting the underlying file under overlayfs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean using realInodePtr
as a new type, and replace all uint64
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, uint64 is fine for me. I meant in the comment above and elsewhere in the file.
// ReadRealInodeFromFd uses ebpf to read the f_inode pointer from the | ||
// kernel "struct file" associated with the given fd. | ||
// Specifically, if fd belongs to overlayFS, it will return the underlying, real inode. | ||
func ReadRealInodeFromFd(fd int) (uint64, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could ReadRealInodeFromFd
be tested in an unit test? We would not test whether the pointer has the correct value, but rather that the comparison between two realInodePtr is correct. The test would need to setup two temporary overlay filesystems with the same lower layer.
This could be in a separate PR, so this PR is not delayed too much.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a unittest for kfilefields
, if the testing framework could support this, I'd like to do so.
0fd19d9
to
9554911
Compare
Signed-off-by: Tianyi Liu <i.pear@outlook.com> Signed-off-by: Alban Crequy <albancrequy@linux.microsoft.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! LGTM from code review. I haven't tested it yet. Let's see the results of the CI first.
I prepared the following workload:
I started the workload on the host filesystem:
And traced with and without filtering options:
The container filtering and enrichment worked fine and there are no duplicate events. Then, I tried the workload again but on the overlay filesystem:
It worked fined as well. |
pkg/uprobetracer: use kfilefields to identify inodes
This patch introduced
private_inode
field to pkgkfilefields
,and it's then used in
uprobetracer
to get an unique inode-ID for each fd.Testing
Run many containers sharing the same image, and see if the records are duplicate.
Run many containers not sharing the same image, and see if any record is missing.
Run programs by
docker exec
, try multi-process applications, and see if any record is missing.