Why do strace's timings for -c and -T disagree? - linux

I have an rsync running a no-op (all files are already there) directory copy operation to a network-mounted file system.
Because all files are already there, the only thing that rsync does is lstat() syscalls.
If I strace -c this, I get this:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00 0.076780 30 2524 lstat
------ ----------- ----------- --------- --------- ----------------
100.00 0.076780 2524 total
real 0m5.451s
But if I strace -T (showing time per syscall), I get this:
lstat("file1", {st_mode=S_IFREG|0644, st_size=32820, ...}) = 0 <0.005523>
lstat("file2", {st_mode=S_IFREG|0644, st_size=20816, ...}) = 0 <0.001529>
lstat("file3", {st_mode=S_IFREG|0644, st_size=1828312, ...}) = 0 <0.001991>
lstat("file4", {st_mode=S_IFREG|0644, st_size=1823258, ...}) = 0 <0.001326>
lstat("file5", {st_mode=S_IFREG|0644, st_size=32820, ...}) = 0 <0.006562>
lstat("file6", {st_mode=S_IFREG|0644, st_size=22578, ...}) = 0 <0.002151>
lstat("file7", {st_mode=S_IFREG|0644, st_size=32835, ...}) = 0 <0.001705>
lstat("file8", {st_mode=S_IFREG|0644, st_size=25493, ...}) = 0 <0.001492>
lstat("file9", {st_mode=S_IFREG|0644, st_size=1783930, ...}) = 0 <0.001974>
The times are completely off!
-c claims each lstat takes roughly 30 usecs/call, while -T shows about 2 ms/call.
The 2 ms make sense, that's in the order of ping speed for the network mount, but 30 microseconds is just plain impossible.
Why is the value in the usecs/call column bogus? Am I misunderstanding it?

From the strace man page:
-c Count time, calls, and errors for each system call and report a summary on program exit. On Linux, this attempts to show
system time (CPU time
spent running in the kernel) independent of wall clock time. If -c is used with -f or -F (below), only aggregate
totals for all traced processes
are kept.
(Emphasis added by me.) Most I/O will just make the actual asynchronous call and context switch away, rather than doing some kind of busy loop. -T will instead show the wall clock time duration between calling into the kernel, and that call returning.
Edit: In later versions, -w gives you wait times, rather than system times, so -c -w will give you times that should match -T.

Related

crontab with ed by commands on stream, results in "no modification made"

I am trying to append a line to my crontab file. I know there are other ways to work around this problem, but still want to know what caused it. The command is run on raspberry pi 3 B+, raspbian lite is installed, with GNU ed 1.15, cron 3.0pl1-134+deb10u1.
The command that I'm stuck on is this:
$ echo -e 'a\n#asdf\n.\nwQ' | EDITOR=ed crontab -e
902
909
No modification made
I'm expecting it to add line #asdf at the end of my crontab file, but it doesn't.
Setting EDITOR='tee -a' as suggested on https://stackoverflow.com/a/30123606/8842387 does not solve the problem. So I guess it is the problem with cron.
Strangely enough, when I give ed commands from the keyboard directly, rather than streaming it, it just works. Maybe subshell creation caused the problem?
Here I'm attaching a few of the last lines from strace result.
$ echo -e 'a\n#asdf\n.\nwQ' | EDITOR=ed strace crontab -e
execve("/usr/bin/crontab", ["crontab", "-e"], 0x7ee54c14 /* 29 vars */) = 0
access("/etc/suid-debug", F_OK) = -1 ENOENT (No such file or directory)
...
read(3, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\7\0\0\0\7\0\0\0\0"..., 4096) = 659
_llseek(3, -393, [266], SEEK_CUR) = 0
read(3, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\7\0\0\0\7\0\0\0\0"..., 4096) = 393
close(3) = 0
getpid() = 18579
socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 3
connect(3, {sa_family=AF_UNIX, sun_path="/dev/log"}, 110) = 0
send(3, "<78>Nov 20 15:31:25 crontab[1857"..., 56, MSG_NOSIGNAL) = 56
openat(AT_FDCWD, "crontabs/pi", O_RDONLY) = -1 EACCES (Permission denied)
openat(AT_FDCWD, "/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 4
fstat64(4, {st_mode=S_IFREG|0644, st_size=2995, ...}) = 0
read(4, "# Locale name alias data base.\n#"..., 4096) = 2995
read(4, "", 4096) = 0
close(4) = 0
openat(AT_FDCWD, "/usr/share/locale/en_GB.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en_GB.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en_GB/LC_MESSAGES/libc.mo", O_RDONLY) = 4
fstat64(4, {st_mode=S_IFREG|0644, st_size=1433, ...}) = 0
mmap2(NULL, 1433, PROT_READ, MAP_PRIVATE, 4, 0) = 0x76f50000
close(4) = 0
openat(AT_FDCWD, "/usr/share/locale/en.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
write(2, "crontabs/pi/: fdopen: Permission"..., 39crontabs/pi/: fdopen: Permission denied) = 39
exit_group(1) = ?
+++ exited with 1 +++
openat(AT_FDCWD, "crontabs/pi", O_RDONLY) = -1 EACCES (Permission denied) looks a bit suspicious, but not sure why it opens the file read-only.
EDIT:
As suggested by #tink, I ran EDITOR=ed strace crontab -e to see what strace gives on an interactive session. The result was almost same (only varying on pid and fd numbers).
I noticed that running echo "..." | EDITOR=ed crontab -e exited with message No modification made but with strace the process halts without any messages. (EDITOR=ed strace crontab -e 2>&1 | grep "No mod" prints nothing). Guess the strace triggers different errors.
Following up on my VISUAL comment, these worked for me:
( unset VISUAL; printf '%s\n' a '#abcd' . wq | EDITOR=ed crontab -e )
printf '%s\n' a '#abcd' . wq | VISUAL=ed crontab -e
In my environment, both VISUAL and EDITOR are set to "vim"
Or, more roundabout, but don't need to monkey with env vars. This one also allows you to do it silently:
crontab <(printf '%s\n' a '#asdf' . '%p' | ed -s <(crontab -l))
I was doing the above on a Mac. On Linux, I can reproduce your observations, but can't explain them.
A small tweak to the last command works:
printf '%s\n' a '#asdf' . '%p' Q | ed -s <(crontab -l) | crontab -
TLDR; (sleep 1; echo -e 'a\n#asdf\n.\nwQ') | EDITOR=ed crontab -e works!
The problem was on crontab.
When I invoke crontab -e it creates a temporary copy of the user cron table in /tmp directory.
Then opens the temporary file with an editor specified by $EDITOR.
After the editing is done, crontab check if the file modification date have changed since its creation.
This is implemented in the patch that enables editing cron table via temporary file.
In my case, ed getting its command from stdin finished the editing too fast so that even a single digit of the modification timestamp of the temporary file had not been changed.
As crontab considered no human can make edition that fast, it assumes no modification made and discards it.
To bypass this behavior, I added sleep 1 before the release of the command.
This will hold ed to wait for its command from stdin after crontab created tempfile, which effectively lets the modification timestamp different.

What happens internally when `ls *.c` is executed?

I've got very interested in Linux internals recently, and currently trying to understand how things work.
I knew that when I type ls
opendir() - function is called;
readdir() - function called for each directory entry in directory data store;
stat() - function can be called to get additional information on files, if required.
Please correct me if I'm missing something or if it's wrong.
The part which is mystery to me is filename expansion(globbing).
I've compared the output of strace ls
open(".", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFDIR|0755, st_size=270336, ...}) = 0
getdents(3, /* 14 entries */, 32768) = 440
getdents(3, /* 0 entries */, 32768) = 0
close(3) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
write(1, "2q.c ds.c fglob fnoglob\n", 272q.c ds.c fglob fnoglob
and strace ls *.c,
stat("2q.c", {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
lstat("2q.c", {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
stat("ds.c", {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
lstat("ds.c", {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
write(1, "2q.c ds.c\n", 112q.c ds.c
) = 11
and from my limited knowledge can tell that in the first case, it indeed behaves as I expected open, stat followed by getdents.
But the later one, with shell globbing isn't clear to me, because it there's already a list of files, which match the pattern. Where does this list came from?
Thanks!
Shell globbing patterns on the command line are expanded by the shell before the utility is invoked.
You can see this by enabling tracing in the shell with set -x:
$ set -x
$ ls -l f*
+ ls -l file1 file2 file3
-rw-r--r-- 1 kk wheel 0 May 11 16:49 file1
-rw-r--r-- 1 kk wheel 0 May 11 16:49 file2
-rw-r--r-- 1 kk wheel 0 May 11 16:49 file3
As you can see, the shell tells you what command it invokes (at the + prompt), and at that point it has already expanded the pattern on the command line.
The ls command does not do filename globbing. In fact, if you single quote the globbing pattern to protect it from the shell, ls is bound to be confused:
$ ls -l 'f*'
+ ls -l f*
ls: f*: No such file or directory
(unless there's actually something in the current directory called f* of course).

Measure total time spent by a process on IO

While running time command, one of the programs gives following output:
real 1m33.523s
user 0m15.156s
sys 0m1.312s
Here the real and user+sys time have a lot of difference. This is most likely due to time spent on IO wait/calls. I want to measure total time spend by program in IO wait or IO calls. Is there any way to do that?
I tried using iotop. However, it doesnot report total time spent by the program performing IO.
Yes, strace - which can provide per-system-call statistics.
Example 1
I want to measure time spent on I/O while accessing stackoverflow.com:
$ time curl stackoverflow.com >/dev/null 2>&1
curl stackoverflow.com > /dev/null 2>&1 0.00s user 0.01s system 2% cpu 0.392 total
OK, 2% CPU and 0.01 s in system. Let's find out:
$ strace -c curl stackoverflow.com >/dev/null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 240k 0 240k 0 0 127k 0 --:--:-- 0:00:01 --:--:-- 130k
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
54.12 0.005497 11 506 write
18.16 0.001845 43 43 fstat
11.95 0.001214 30 41 poll
5.75 0.000584 32 18 recvfrom
3.40 0.000345 3 101 mmap
2.51 0.000255 4 62 mprotect
1.98 0.000201 4 50 close
1.84 0.000187 31 6 getsockname
0.29 0.000029 1 42 1 open
Especially useful compare this results with results measured for runing curl without args.
Anyway. strace shows that curl mostly spends time in write, fstat and poll.
Another example
The first approach seems show incorrect results for sleep. If you are not satisfied with the first approach you can just print get times of each syscall (strace -T). Get this data and process them to find summary time of each syscall.
$ strace 2>&1 -T curl stackoverflow.com >/dev/null | head -n 20
execve("/usr/bin/curl", ["curl", "stackoverflow.com"], [/* 62 vars */]) = 0 <0.000219>
brk(0) = 0x186e000 <0.000175>
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc04c9e6000 <0.000166>
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) <0.000238>
open("/etc/ld.so.cache", O_RDONLY) = 3 <0.000144>
fstat(3, {st_mode=S_IFREG|0644, st_size=96498, ...}) = 0 <0.000175>
mmap(NULL, 96498, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fc04c9ce000 <0.000164>
close(3) = 0 <0.000160>
open("/usr/lib64/libcurl.so.4", O_RDONLY) = 3 <0.000047>
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240\333\300\">\0\0\0"..., 832) = 832 <0.000160>
fstat(3, {st_mode=S_IFREG|0755, st_size=346008, ...}) = 0 <0.000216>
mmap(0x3e22c00000, 2438600, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x3e22c00000 <0.000189>
mprotect(0x3e22c51000, 2097152, PROT_NONE) = 0 <0.000032>
mmap(0x3e22e51000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x51000) = 0x3e22e51000 <0.000119>
close(3) = 0 <0.000110>
open("/lib64/libidn.so.11", O_RDONLY) = 3 <0.000257>
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0/#U1\0\0\0"..., 832) = 832 <0.000051>
fstat(3, {st_mode=S_IFREG|0755, st_size=209088, ...}) = 0 <0.000041>
mmap(0x3155400000, 2301736, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x3155400000 <0.000037>
mprotect(0x3155432000, 2093056, PROT_NONE) = 0 <0.000037>

How does bash knows to read autocomple from /usr/lib/git-core/

I installed git-flow, which copied some bash scripts to /usr/lib/git-core/ which are now available in bash complete.
I know that bash reads the following directories looking for autocomplete scripts:
/etc/bash_completion.d/
/usr/share/bash-completion/
What makes bash aware to those new scripts?
I installed git-flow from the debian repositories, and I am trying to understand what makes this package ticks.
I did not succeed in finding where bash determines it needs to read this directory. Looking through /usr/share/bash-completion/completions/git did not help either.
update
Looking around, it seems that /usr/git-core/git and /usr/bin/git are the same binary file:
~ $ file /usr/bin/git
/usr/bin/git: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=4f634dadca99fb29ed72568d5604df087d6e6502, stripped
~ $ file /usr/lib/git-core/git
/usr/lib/git-core/git: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=4f634dadca99fb29ed72568d5604df087d6e6502, stripped
OK, given the hint from Etan, it seems, that what seems to be a bash completion is actually a git completion. As far why /usr/lib/git-core is search, strace shows that it's hard coded when configuring and building the package:
execve("/usr/bin/git", ["git", "help", "-a"], [/* 73 vars */]) = 0
brk(0) = 0xab4000
...
openat(AT_FDCWD, "/usr/lib/git-core", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
getdents(3, /* 169 entries */, 32768) = 6192
stat("/usr/lib/git-core/git-fetch-pack", {st_mode=S_IFREG|0755, st_size=1577256, ...}) = 0
stat("/usr/lib/git-core/git-web--browse", {st_mode=S_IFREG|0755, st_size=4398, ...}) = 0
stat("/usr/lib/git-core/git-upload-archive", {st_mode=S_IFREG|0755, st_size=1577256, ...}) = 0
stat("/usr/lib/git-core/git-credential-store", {st_mode=S_IFREG|0755, st_size=765192, ...}) = 0
...
When compiling the package from source with configure --prefix=/usr/local/bin/git the strace log is:
execve("/usr/local/git/bin/git", ["/usr/local/git/bin/git", "help", "-a"], [/* 73 vars */]) = 0
brk(0) = 0x2395000
...
openat(AT_FDCWD, "/usr/local/git/libexec/git-core", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
getdents(3, /* 159 entries */, 32768) = 5792
stat("/usr/local/git/libexec/git-core/git-fetch-pack", {st_mode=S_IFREG|0755, st_size=8448439, ...}) = 0
stat("/usr/local/git/libexec/git-core/git-web--browse", {st_mode=S_IFREG|0755, st_size=4398, ...}) = 0
stat("/usr/local/git/libexec/git-core/git-upload-archive", {st_mode=S_IFREG|0755, st_size=8448439, ...}) = 0
stat("/usr/local/git/libexec/git-core/git-credential-store", {st_mode=S_IFREG|0755, st_size=4146627, ...}) = 0
stat("/usr/local/git/libexec/git-core/git-p4", {st_mode=S_IFREG|0755, st_size=122122, ...}) = 0
stat("/usr/local/git/libexec/git-core/git-merge-tree", {st_mode=S_IFREG|0755, st_size=8448439, ...}) = 0
stat("/usr/local/git/libexec/git-core/git-mergetool", {st_mode=S_IFREG|0755, st_size=8377, ...}) = 0
The need to read /etc/bash_completion.d is built into bash which sources all scripts contained within this directory making any defined functionality available.
Each time you start a new terminal, this directory is re-read as part of the bash warmup procedure making all defined completions available to the shell.
Different vendors may have different directories read alongside /etc/bash_completion.d including /usr/share/bash-completion but this one is the minimum defined.
If you want to define your own completions, have a look at https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html.

what functions are called when i do vi

When I do a vi filename from the command prompt, what fuse functions are called if I am using the fusexmp example ? I could guess mknod, open are called.
When I do a write ie when i do :wq write is called. is that right.
There's no fantastically easy way to see which FUSE functions are called for any given file operation, but running strace(1) will record the system calls, which is quite close to the FUSE functions:
$ strace -o /tmp/vim.all vim /etc/motd
A lot of those system calls aren't related to the one file specifically, but to the process of loading vim, its dynamically linked libraries, your local configuration, and all its supporting files.
Here's some selected lines that refer to the /etc/motd that I opened:
stat("/etc/motd", {st_mode=S_IFREG|0644, st_size=183, ...}) = 0
stat("/etc/motd", {st_mode=S_IFREG|0644, st_size=183, ...}) = 0
stat("/etc/motd", {st_mode=S_IFREG|0644, st_size=183, ...}) = 0
stat("/etc/motd", {st_mode=S_IFREG|0644, st_size=183, ...}) = 0
access("/etc/motd", W_OK) = -1 EACCES (Permission denied)
open("/etc/motd", O_RDONLY) = 7
close(7) = 0
open("/etc/motd", O_RDONLY) = 7
read(7, "Welcome to Ubuntu 11.04 (GNU/Lin"..., 8192) = 183
read(7, "", 65536) = 0
close(7) = 0
stat("/etc/motd", {st_mode=S_IFREG|0644, st_size=183, ...}) = 0
The intervening lines make the repeated stat(2) calls a little less silly looking.

Resources