4

I am manually tweaking Debian init scripts in order to optimize the boot time of an embedded device. So far I reduced it by half just with the "low hanging fruits" ie. the smaller scripts that are easy to understand.

Now I am left with a few init scripts that take about 20s to run in total. Based on the experience from the other scripts, I can gain another 5 to 10 seconds here. The problem is those scripts are a bit too hard for me to understand because they source several helper scripts from /lib/init/ which are difficult to follow (mount-functions.sh being the first culprit).

In order to make those remaining scripts easier to understand so that I can optimize them, I'd like some way to "pre-process" those init scripts so that whenever they source a file it gets inlined in the script itself (recursively of course), and remove the unused functions to unclutter the code. Is there a tool for this?

  • One of the first things to look at would be to make sure /bin/sh is not bash. mksh probably your best bet. removing dead code or moving into one file is not likely to make much difference. – Stéphane Chazelas Oct 4 '13 at 21:23
  • @StephaneChazelas I should have been more clear, my bad. Flattening and removing dead code is not meant to make the script faster, but to make it simpler for me to understand so that I can make it faster. For what it's worth, simply removing the sourcing of /lib/init/init-functions (where possible) removes 0.5 to 1 second per script. But the remaining scripts I have are too intricate for me to simply cut through them like I did for the rest, hence my request. As to /bin/sh it's already symlinked to dash so there's not much room for improvement here. – syam Oct 4 '13 at 21:32
  • 2
    Maybe a quick and dirty way to do it would be to chroot into a dummy directory, set -x then source your script while dumping the precious debugging information to a file for easy perusal. – Joseph R. Oct 4 '13 at 21:48
  • bashdb could help you. – Stéphane Chazelas Oct 4 '13 at 21:59
  • @JosephR. Thanks, your set -x suggestion helped me a lot making sense of what was going on, along with slm's answer. – syam Oct 5 '13 at 22:54
2

You could do something like this. I've only lightly tested it but I think the general concept is sound.

Example

Say you have the following directory of files:

$ ls -l
total 16
-rwxrwxr-x 1 saml saml 268 Oct  4 17:44 expander.bash
-rw-rw-r-- 1 saml saml  18 Oct  4 16:49 src_1.bash
-rw-rw-r-- 1 saml saml  20 Oct  4 16:50 src_2.bash
-rwxrwxr-x 1 saml saml 119 Oct  4 16:49 top.bash

The top.bash file looks like this:

$ cat top.bash 
#!/bin/bash

echo "1"
echo "2"
echo "3"
echo "4"

. src_1.bash

echo "15"
echo "16"

. src_2.bash

echo "27"
echo "28"

You could use the following script, expander.bash to "expand" top.bash:

#!/bin/bash

while read -r line; do
  if echo "$line" | grep -q '\. .*'; then
    file=$(echo "$line" | sed 's/\. //')
    echo "### below sourced from: $file"
    cat $file
    echo "### above sourced from: $file"
    continue
  fi
  printf "%s\n" "$line"
done < top.bash

Example Run

$ ./expander.bash 
#!/bin/bash

echo "1"
echo "2"
echo "3"
echo "4"

### below sourced from: src_1.bash
echo "6"
echo "7"
### above sourced from: src_1.bash

echo "15"
echo "16"

### below sourced from: src_2.bash
echo "17"
echo "18"
### above sourced from: src_2.bash

echo "27"
echo "28"

Potential Enhancements?

For one I used grep and sed, those could be swapped out to make it a more pure Bash solution. I didn't take the time to do this, since this is a rough prototype.

The second area that will most likely need attention is the scripts ability to figure out that a sourcing of another file is occurring. The pattern that detects this will likely need to be "tweaked" based on your situation.

| improve this answer | |
  • Ah of course. I was looking for an existing tool like the cpp preprocessor (just for shell scripts, not C) but your answer shows how easy it is to make one ourselves. I'll try this and the other suggestions before accepting, but this looks promising. – syam Oct 4 '13 at 22:12
  • Thanks, it's been quite helpful (along with set -x to help me understand what was really going on). For the record, the boot time went from 45s before any optimization down to 11s now. I'd tend to call that a frank success. :) – syam Oct 5 '13 at 22:09
  • @syam - Thanks for letting us know. That's terrific. Thanks for the question! – slm Oct 5 '13 at 22:31
  • To be honest, that was at the cost of all modularity: I "simply" removed all corner cases that didn't apply to my device. The thing is, I'm CPU-bound even before being disk-bound so any computation avoided is a huge win (again, just removing the sourcing of /lib/lsb/init-functions and the corresponding log_* calls saves .5 to 1 second per script, this alone did half the job). But it shows that Debian is flexible and can be optimized quite a lot. :) – syam Oct 5 '13 at 22:41
0

@slm's answer inspired me to write flatten.sh.

flatten.sh will not include the whole file to be sourced. Instead, it will only fetch those functions defined in the file, that are actually used in the script being flattened.

This is useful if you keep a file "lazy.lib" which in turn sources ALL the functions you use. Just source "lazy.lib" in your script while developing and flatten it when you're done.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.