RFC: lsfd, a brand new Linux specific replacement for lsof#1418
RFC: lsfd, a brand new Linux specific replacement for lsof#1418masatake wants to merge 181 commits intoutil-linux:masterfrom
Conversation
|
Thanks for your work, it seems really good. I have pushed this to the topic/lsfd branch, we can share this branch to work on lsfd before it will be merged to the master branch (I'll work on it in the next few days too). Notes (which list;-)):
seems like we can use INOTIFY, SIGNAL, and TIMER there Why the same file for the same task is printed more than once: it would be nice to add a hint about the difference to the output. Now it's confusing.
|
|
Thank you for the comment.
You don't know about my private plan for RHEL13 :-P I think we should add filtering API to llibsmartcols first. About lsfd, just adding the API to llibsmartcols is not enough. |
LOL, would you please say more about? ;-)
Yes, in many cases it makes sense to use the libscols_table as a container where the application keeps data (and in some cases, it's impossible, for example in lsblk the tree of data is too complex and in lscpu the data capturing is very tricky.)
I more and more think about "liblsdata" to handle also data capturing part of the applications. Now many operations Maybe it would be also possible to replace with: not sure, and maybe it's overengineering :-)
Yes. |
|
Now, when I think about a way how to describe the data capturing, then it seems like a nonsense. It's better to keep all the |
|
@masatake hi, why do you read /proc/%d/map_files/ as well as /proc/%d/maps? It seems the "maps" file contains all the information, or is it possible that filenames in the "maps" file are truncated? |
Just my mistake. The code accessing files under map_files should be removed. |
|
@karelzak, could you look at https://github.com/masatake/fd-catalog? |
That's nice! It would be nice to have it in tests/helpers/ directory :-) |
|
In the next few days, I will rewrite lib/procutils.c to make it more usable in lsfd (and other tools). I'd like to have something like we already have for sysfs (lib/sysfs.c); it means code based on process directory file descriptor and "*at" functions to reuse lib/path.c functions. |
|
The more than half of items you listed as review comments about the desing of columns. Based on studying lsof, I planed to design the columns in two levels. The second leve is for providing wel-coocked data for users. I could not complete lsfd before opening this pull request. So the levels are not separated well in My idea of "levels" are very related to your "view". My understanding is you are working on merging lsfd with modifying it acceptable. |
|
Oh, I misunderstood what were happning. You are already working of the lsfd branch. I will watch it. |
|
For now, I'm working on an elegant way how to read data from /proc/pid/ to reduce effort and code duplication in util-linux. The current code in lib/procutils.c is poor and I'd like to have something better (like we already have for sysfs). So, my current intention is not to change how lsfd prints data on output. It will be the next step and I wait for you with it ;-) |
Do you mean I should start making more commits based on your reivew comments? |
Yes, but use topic/lsfd branch if you can. |
I see. |
|
I got one more question about the way to work. |
|
Maybe you can try "git push --force" to this PR. (I mean git push --force to your masatake:lsfd on github.) |
|
I see. Thank you. I will keep this full request fresh. |
|
I have committed other changes to collect_* stuff. The last remaining stuff is maps and mountinfo. I'll do it tomorrow. We need to parse mountinfo only once for the mount namespace. It's overkill to do it for each process. |
|
We should reflect the result of filtering to the exit status: lsof-org/lsof#128. |
|
The topic/lsfd branch updated, thanks! |
|
Sorry for the delay last week (I had a vacation and after then I had an issue with my workstation disk...). Today I committed improved /proc/#/maps code, now it does not use /proc/#/map_files/ symlinks. For this week my TODO is to reduce the number of situations we call stat(), because there are so many duplications $ ./lsfd -oNAME | wc -l It would be probably better to have two structs
|
|
The initial version of the display filter engine works. There was a choice implementing filter options based on a filtering engine working in the stage of information-collecting time (collecting filter). For making the execution time of lsfd shorter, the collecting filter is much better than the display filter. However, the collecting filter may be tightly coupled with the core of lsfd. It is much hard to implement the collecting filter than the display filter. On the other hand, we can implement the display filter based on libsmarcols. We don't need any knowledge about lsfd to implement the display filter. Here is an example session: # ./lsfd -D '(PID == 1) && (ASSOC == "exe")'
COMMAND PID USER ASSOC MODE TYPE SOURCE MNTID INODE NAME
systemd 1 root exe --- REG dm-0 0 1847406 /usr/lib/systemd/systemd
# ./lsfd -D '(PID == 1) && ((ASSOC == "exe") or (ASSOC == "1"))'
COMMAND PID USER ASSOC MODE TYPE SOURCE MNTID INODE NAME
systemd 1 root exe --- REG dm-0 0 1847406 /usr/lib/systemd/systemd
systemd 1 root 1 rw- CHR mem:3 25 4 /dev/null
# ./lsfd -D '(PID == 1) && ((ASSOC == "exe") or (ASSOC == "1") or (SOURCE == "tmpfs"))'
COMMAND PID USER ASSOC MODE TYPE SOURCE MNTID INODE NAME
systemd 1 root exe --- REG dm-0 0 1847406 /usr/lib/systemd/systemd
systemd 1 root 1 rw- CHR mem:3 25 4 /dev/null
systemd 1 root 120 rw- FIFO tmpfs 29 752 /run/dmeventd-server
systemd 1 root 121 rw- FIFO tmpfs 29 753 /run/dmeventd-client
systemd 1 root 399 rw- FIFO tmpfs 29 757 /run/initctl
# ./lsfd -D '(PID == 1) && ((ASSOC == "exe") or (ASSOC == "1") or (TYPE == "FIFO"))'
COMMAND PID USER ASSOC MODE TYPE SOURCE MNTID INODE NAME
systemd 1 root exe --- REG dm-0 0 1847406 /usr/lib/systemd/systemd
systemd 1 root 1 rw- CHR mem:3 25 4 /dev/null
systemd 1 root 120 rw- FIFO tmpfs 29 752 /run/dmeventd-server
systemd 1 root 121 rw- FIFO tmpfs 29 753 /run/dmeventd-client
systemd 1 root 391 r-- FIFO pipefs 14 18206 pipe:[18206]
systemd 1 root 399 rw- FIFO tmpfs 29 757 /run/initctl
./lsfd -J -D '(PID == 1) && (ASSOC == "exe")'
# ./lsfd -J -D '(PID == 1) && (ASSOC == "exe")'
{
"lsfd": [
{
"command": "systemd",
"pid": 1,
"user": "root",
"assoc": "exe",
"mode": "---",
"type": "REG",
"source": "dm-0",
"mntid": 0,
"inode": 1847406,
"name": "/usr/lib/systemd/systemd"
}
]
}
# ./lsfd -D 'NAME == "/etc/passwd"'
COMMAND PID USER ASSOC MODE TYPE SOURCE MNTID INODE NAME
fd-catalog 1677961 yamato 3 r-- REG dm-0 88 271210 /etc/passwdUnlike using grep, the table is enough compact to the data. I will polish the display filter code. |
misc-utils/lsfd.c
Outdated
| map->exec = x; | ||
| map->shared = s; | ||
| /* first try the path */ | ||
| if (stat(path, &sb) < 0) |
There was a problem hiding this comment.
Though I agreed with you that we don't need map_file, this line tells us the limitation of maps.
This line tries to get sb for path in the current mnt namespace.
If the fs tree is rearranged with bind mount after the target process opening a file, lsfd can be cheated.
map_files is reliable.
And this may be the reason why map_file is introduced to Linux kernel.
There was a problem hiding this comment.
Good point.
Frankly, I'm not sure how does it work with over-mount. This is something we need to test, I think we can detect the problem as the maps file contains devno and inode, so we can compare these numbers with the result from stat(), and in case of any problem we can try /proc/#/map_files/xxxx-xxx symlink as a fallback solution. I'll add TODO comment to the code.
The namespaces are another story, I guess readlink() for /proc/#/map_files/xxxx-xxxx is also interpreted in the current namespace. The ideal solution for all files would be to detect that process uses an unshared mount namespace and use setns() to enter the namespace before we start to collect files.
Definitely something we need to care about, but later ;-)
There was a problem hiding this comment.
Frankly, I'm not sure how does it work with over-mount. This is something we need to test, I think we can detect the problem as the maps file contains devno and inode, so we can compare these numbers with the result from stat(), and in case of any problem we can try /proc/#/map_files/xxxx-xxx symlink as a fallback solution. I'll add TODO comment to the code.
When testing, we should add btrfs as a target platform. It seems that a devno on btrfs is not so consistent.
See lsof-org/lsof#152
The namespaces are another story, I guess readlink() for /proc/#/map_files/xxxx-xxxx is also interpreted in the current namespace.
As you wrote, if you pass the path getting from readlink() to stat(), it doesn't make sense.
However, I expect doing fstatat /proc/#/map_files/xxxx-xxx doesn't do any filename-resolution.
I will test it.
Definitely something we need to care about, but later ;-)
Yes.
I think it's better to start with a display filter. I don't think we need a collecting filter which able to evaluate individual entries. It seems better to have a way how to enable/disable some parts of the data collecting. For example, disable memory maps parsing, disable /proc/#/fds, or disable socket, etc. ... then we can later evaluate display filter and optimize collecting. For example for display filter
This is an interesting idea, because it is something that we can use in all libsmartcols tools ;-)
I guess people will argue against display filters by awk where you can compare columns, but the problem with grep and awk is that it uses displayed data, it means already formatted, etc. The JSON is a good example. Another thing is that applications can also use display filter to optimize data collecting (my previous note). |
|
Yes, devno for btrfs sucks for years ... (see libmount/findmnt where we have extra optimizations for btrfs); there is an attempt to fix it in lkml, but not sure what is the current status. |
|
@karelzak, I pushed the changes adding libsmartcols based filtering. I hope you like them. Like lsfd itself, the changes may require your massive revising |
|
At the first glance, the output filter seems cool -- I do not test it yet, that's my plan for tomorrow and I'll probably merge it to topic/lsfd after that, ok? I've tried to split "struct file" into "struct file" and "struct file_reference" to share information about files between processes (to reduce stat() calls), but the result was unsuccessful. The Linux stat() is so fast for already open files that the benefit of stat() calls reduction is the same as overhead introduced by a maintenance of the global files list in lsfd :-) But I have found a more effective way how to reduce 50% of stat() calls, in /proc/#/maps the same file is usually mapped more than once, so reuse the previous "struct file" is simple without any global list of files -- see commit cf2d4b1. |
|
To implement --summary, I'm working on adding The current implementation archives:
However, it doesn't:
To implement these, we need the "pivot" table feature as we see in spreadsheet software like Excel. |
$ time sudo ./lsfd -Q '(PID == 1) or (PID == 2)' > /dev/null
real 0m0.508s
user 0m0.230s
sys 0m0.267s
$ time sudo ./lsfd -p 1,2 > /dev/null
real 0m0.088s
user 0m0.036s
sys 0m0.033s
$ [ $(./lsfd -p 1,2) = $(./lsfd -Q '(PID == 1) or (PID == 2)') ]
$ echo $?
0
Signed-off-by: Masatake YAMATO <yamato@redhat.com>
Signed-off-by: Masatake YAMATO <yamato@redhat.com>
It's unnecessary to redirect to any /proc dump if we can use 'test_mkfds'. Signed-off-by: Karel Zak <kzak@redhat.com>
|
Merged, thanks.
but works as expected.
and the default for -S, --summary will be some predefined counters with human-readable strings. Sounds good. I don't think we need pivot table, just compose an array of strings (see include/strv.h) and before printing convert it to |
Signed-off-by: Masatake YAMATO <yamato@redhat.com>
Signed-off-by: Masatake YAMATO <yamato@redhat.com>
Signed-off-by: Masatake YAMATO <yamato@redhat.com>
Signed-off-by: Masatake YAMATO <yamato@redhat.com>
Signed-off-by: Masatake YAMATO <yamato@redhat.com>
Unintentionally they were extern'ed. Signed-off-by: Masatake YAMATO <yamato@redhat.com>
Signed-off-by: Masatake YAMATO <yamato@redhat.com>
Signed-off-by: Masatake YAMATO <yamato@redhat.com>
Signed-off-by: Masatake YAMATO <yamato@redhat.com>
User may wonder how the built-in counters are defined. Describing their definitions in lsfd(1) is one of choice. However, the definitions may drastically change in the future development. --dump-counters option is for making lsfd self descriptive. Users who understand the filter expressions can understand directly the meaning of the counters. Signed-off-by: Masatake YAMATO <yamato@redhat.com>
|
I found implementing the feature, what I called "pivot," is not so easy.
It is not easy for me to improve it. |
|
Merged. I have also done small changes to the --summary semantic to make it more user-friendly (make --summary=only the default when argument not specified). I'll also improve JSON support in libsmartcols to support more tables in one JSON object. |
|
@karelzak, thank you for reviewing and merging. There are still many TODO items listed in the first comment in this pull request, but lsfd can be used for daily work already. Hereafter, I would like to open separate pull requests topic by topic. Though I am a member of this organization, I would like to use a topic branch of my repository (masatake/util-linux) to make a pull request. I'm not sure this method is popular or not. I have used this method in the project I have maintained (universal-ctags/ctags), and it is comfortable for me. For a while, when making a pull request, I will target "topic/lsfd" branch of "util-linux" repository in "util-linux" organization for merging. If you have an idea about the target branch, let me know. Though this pull request will be closed, I will maintain the TODO lists in the first comment. |
|
If you find a critical issue for including lsfd in the next release, let me know. |
|
I think we can merge topic/lsfd to the master branch and you can continue with your changes and PRs against the master branch. It will help us to gather more feedback from others developers and users. The code is good enough, release early, release often ... ;-) My plan is to mark lsfd as "experimental" for the next release. It means we can change command-line options and semantic of the command without caring about backward compatibility. |
I see. I will do so. |
|
Merged to the master branch. |
As you noticed at lsof-org/lsof#89, I have developed lsfd.
lsfd is a command like lsof. The critical differences are (1) lsfd is specialized to Linux, and (2) lsfd is based on libsmartcols.
This pull request is for getting comments.
Socket-related information is not extracted yet.
The filtering feature that lsof is not implemented yet.
Test cases are not available yet.
The man page is not available yet.
They are parts of future work.
A quirk in this pull request is about POSIX threads.
I assumed using multiple threads may improve the performance of listing.
After implementing and measuring the execution time, I found the performance is not so improved as I expected.
So I deleted the code for multi-threading.
Anyway, I made so many commits. I would like to get comments before going ahead more.
example output:
check list taken from @karelzak's [comments](https://github.com//pull/1418#issuecomment-905455422):
it would be nice to avoid generic struct names like "file", it would be better to have for example "struct lsfd_file", etc.the request for change is canceld... it would be nice to add a hint about the difference to the output. Now it's confusing.
add filter by TYPE (lsfd --type REG)→ write the way to do the same with -Q option to lsfd.1.add filter by SOURCE (lsfd --source sda1)→ write the way to do the same with -Q option to lsfd.1.add filter by DEVTYPE (lsfd --devtype blk)→ write the way to do the same with -Q option to lsfd.1.add filter by COMMAND (lsfd --command system)→ write the way to do the same with -Q option to lsfd.1.add filter by task type, userspace vs. kerne threads -- see ps(1) and commands with [name] (lsfd --kernel | --userspace)add --deleted and --nodeleted→ write the way to do the same with -Q option to lsfd.1.my check list:
--no-wrapoptionlsfd -- fname1 fname2 ...