Skip to main content

Profile the execution speed of Bash script

Project description

L_bash_profile

L_bash_profile provides deterministic profiling of Bash programs. A profile is contains the whole trace of execution of a particular bash script with timestamp of each executed command. Such profile can be formatted into reports to find code hot-spots.

Example

$ L_bash_profile profile -o ./profile.txt 'i=0; while ((i < 100)); do ((i++)); done'
PROFILING: i=0; while ((i < 100)); do ((i++)); done to ./profile.txt
PROFING ENDED, output in /dev/stdout
$ L_bash_profile analyze ./profile.txt
Top 3 cummulatively longest commands:
  percent    spent_us  cmd            calls    spentPerCall    topCaller1  topCaller2    topCaller3    example
---------  ----------  -----------  -------  --------------  ------------  ------------  ------------  ---------
50.1513         3_148  ((i < 100))      101         31.1683           101                              ~:13
49.4504         3_104  ((i++))          100         31.04             100                              ~:13
 0.955871          60  i=0                1         60                  1                              ~:13

Script executed in 0:00:00.006277us, 202 instructions, 0 functions.

Features

  • generate dot callgraph
  • print longest commands
  • print longest functions
  • generate python profile file

Installation

pipx install L_bash_profile

Subcommands

profile

Executes a given script under profiling.

Profiling just executes a bash -c script that spcifies DEBUG trap that executes a command that prints timestamp, current command, and context to the given file. After setup, the script executes the commands given on command line.

To profile a script, you have to execute it in current shell context source ./yourscript args.

analyze

Analyses the prifiling information, printing commands and function hotspots.

Additionally it can generate a dot graph from the calls, for example:

$ L_bash_profile profile 'f() { sleep 0.1; }; g() { sleep 0.1; f; f; }; b() { f; g; }; b' | L_bash_profile analyze --dot profile.dot
PROFILING: f() { sleep 0.1; }; g() { sleep 0.1; f; f; }; b() { f; g; }; b to /dev/stdout
PROFING ENDED, output in /dev/stdout
Top 4 cummulatively longest commands:
    percent    spent_us  cmd          calls    spentPerCall  topCaller1    topCaller2    topCaller3    example
-----------  ----------  ---------  -------  --------------  ------------  ------------  ------------  --------------
133.307         405_612  sleep 0.1        4     101403       f 3           g 1                         environment:13
  0.132777          404  f                6         67.3333  f 3           g 2           b 1           environment:13
  0.0374667         114  g                2         57       b 1           g 1                         environment:13
  0.0348375         106  b                2         53       1             b 1                         ~:13

Top 3 cummulatively longest functions:
    percent    spent_us  funcname      calls    spentPerCall    instructions    instructionsPerCall  example
-----------  ----------  ----------  -------  --------------  --------------  ---------------------  --------------
100.093         304_552  f                 3          101517               6                      2  environment:13
 33.3493        101_472  g                 1          101472               4                      4  environment:13
  0.0492983         150  b                 1             150               3                      3  environment:13

Script executed in 0:00:00.304270us, 14 instructions, 3 functions.

Generates a profile.dot file that can be viewed:

exampledot

Example output

Top 20 cummulatively longest commands:
  percent    spent_us  cmd                                                   calls    spentPerCall  topCaller1                          topCaller2                               topCaller3                 example
---------  ----------  --------------------------------------------------  -------  --------------  ----------------------------------  ---------------------------------------  -------------------------  --------------------------
 5.47741      115_763  "${@:2}"                                               1794         64.5279  L_assert2 1025                      L_is_valid_variable_name 293             L_regex_match 293          ../L_lib/bin/L_lib.sh:406
 4.45483       94_151  declare -p _L_optspec                                   174        541.098   _L_argparse_split 159               _L_argparse_parse_args_short_option 15                              ../L_lib/bin/L_lib.sh:2819
 3.70534       78_311  (($#))                                                 1292         60.6122  _L_abbreviation_v 368               L_args_contain 356                       _L_argparse_split 292      ../L_lib/bin/L_lib.sh:3772
 3.23247       68_317  shift                                                  1180         57.8958  _L_abbreviation_v 368               L_args_contain 356                       _L_argparse_split 200      ../L_lib/bin/L_lib.sh:662
 2.54289       53_743  L_assert2 "$(declare -p _L_optspec)" L_var_is_se..      134        401.067   _L_argparse_split 67                L_assert2 67                                                        ../L_lib/bin/L_lib.sh:2728
 1.75324       37_054  L_assert2  L_regex_match "${!3}" "^[^=]*=[(].*[)..      586         63.2321  L_nestedasa_get 293                 L_assert2 293                                                       ../L_lib/bin/L_lib.sh:1828
 1.62293       34_300  L_assert2  L_is_valid_variable_name "$1"                586         58.5324  L_nestedasa_get 293                 L_assert2 293                                                       ../L_lib/bin/L_lib.sh:1827
 1.59913       33_797  [[ "$1" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]                  311        108.672   L_is_valid_variable_name 311                                                                            ../L_lib/bin/L_lib.sh:583
 1.43783       30_388  [[ "$1" =~ $2 ]]                                        293        103.713   L_regex_match 293                                                                                       ../L_lib/bin/L_lib.sh:311
 1.33175       28_146  _L_optspec_declare_p=$(declare -p _L_optspec)            44        639.682   _L_argparse_split 44                                                                                    ../L_lib/bin/L_lib.sh:2826
 1.23456       26_092  declare -n _L_asa="$1"                                  435         59.9816  L_asa_has 375                       L_asa_is_empty 50                        _L_asa_get_v 5             ../L_lib/bin/L_lib.sh:1734
 1.15995       24_515  _L_argparse_parser_next_option _L_i _L_optspec          428         57.278   _L_argparse_parser_next_option 214  _L_argparse_parse_args_set_defaults 108  _L_argparse_parse_args 77  ../L_lib/bin/L_lib.sh:3104
 1.14149       24_125  L_asa_has _L_parser "option$_L_i"                       428         56.3668  _L_argparse_parser_next_option 214  L_asa_has 214                                                       ../L_lib/bin/L_lib.sh:2909
 1.1379        24_049  [[ -n "${_L_asa["$2"]+yes}" ]]                          375         64.1307  L_asa_has 375                                                                                           ../L_lib/bin/L_lib.sh:1735
 1.13501       23_988  eval "$1=${!3#*=}"                                      293         81.8703  L_nestedasa_get 293                                                                                     ../L_lib/bin/L_lib.sh:1829
 1.11774       23_623  [[ $1 != _L_asa ]]                                      435         54.3057  L_asa_has 375                       L_asa_is_empty 50                        _L_asa_get_v 5             ../L_lib/bin/L_lib.sh:1734
 1.03428       21_859  [[ "$1" = "$needle" ]]                                  343         63.7289  L_args_contain 343                                                                                      ../L_lib/bin/L_lib.sh:664
 0.950999      20_099  jq -r .v                                                  6       3349.83    t 6                                                                                                     ../L_lib/bin/L_lib.sh:949
 0.924124      19_531  _L_handle_v "$@"                                        310         63.0032  _L_handle_v 155                     L_abbreviation 74                        L_max 18                   ../L_lib/bin/L_lib.sh:556
 0.910781      19_249  _"${FUNCNAME[1]}"_v "${@:3}"                            288         66.8368  _L_handle_v 144                     _L_abbreviation_v 74                     _L_max_v 18                ../L_lib/bin/L_lib.sh:341

Top 20 cummulatively longest functions:
  percent    spent_us  funcname                               calls    spentPerCall    instructions    instructionsPerCall  example
---------  ----------  -----------------------------------  -------  --------------  --------------  ---------------------  --------------------------
 21.1877      447_794  _L_argparse_split                         92        4867.33             4071               44.25     ../L_lib/bin/L_lib.sh:2685
  6.31623     133_491  L_assert2                               1025         130.235            2050                2        ../L_lib/bin/L_lib.sh:406
  5.26407     111_254  _L_unittest_internal                     175         635.737            1762               10.0686   ../L_lib/bin/L_lib.sh:1910
  4.99153     105_494  L_nestedasa_get                          293         360.048            1465                5        ../L_lib/bin/L_lib.sh:1827
  4.17789      88_298  L_args_contain                           117         754.684            1406               12.0171   ../L_lib/bin/L_lib.sh:662
  4.11675      87_006  L_asa_has                                375         232.016            1500                4        ../L_lib/bin/L_lib.sh:1734
  3.86934      81_777  _L_abbreviation_v                         74        1105.09             1379               18.6351   ../L_lib/bin/L_lib.sh:835
  3.73776      78_996  _L_argparse_parse_args                    27        2925.78             1326               49.1111   ../L_lib/bin/L_lib.sh:3324
  2.82215      59_645  L_argparse                                24        2485.21              880               36.6667   ../L_lib/bin/L_lib.sh:3354
  2.808        59_346  _L_handle_v                              154         385.364             929                6.03247  ../L_lib/bin/L_lib.sh:335
  2.39025      50_517  L_is_valid_variable_name                 311         162.434             622                2        ../L_lib/bin/L_lib.sh:583
  2.34809      49_626  _L_argparse_parser_next_option           214         231.897             856                4        ../L_lib/bin/L_lib.sh:2907
  2.26155      47_797  L_argparse_print_help                     12        3983.08              836               69.6667   ../L_lib/bin/L_lib.sh:2563
  2.1893       46_270  L_regex_match                            293         157.918             586                2        ../L_lib/bin/L_lib.sh:310
  1.62719      34_390  _L_argparse_parser_next_argument         123         279.593             588                4.78049  ../L_lib/bin/L_lib.sh:2920
  1.52002      32_125  L_var_is_set                             163         197.086             489                3        ../L_lib/bin/L_lib.sh:508
  1.41668      29_941  _L_argparse_parse_args_short_option       10        2994.1               260               26        ../L_lib/bin/L_lib.sh:3167
  1.30435      27_567  t                                          5        5513.4                41                8.2      ../L_lib/bin/L_lib.sh:946
  1.23073      26_011  _L_argparse_parse_args_set_defaults       27         963.37              389               14.4074   ../L_lib/bin/L_lib.sh:3104
  1.13184      23_921  _L_list_functions_with_prefix_v            1       23921                 455              455        ../L_lib/bin/L_lib.sh:560

Script executed in 0:00:02.113461us, 28867 instructions, 123 functions.

Example2

$ L_bash_profile profile -n500 -b 'f() { "$@"; }; g() { "$@"; }; i=1' 'f eval "(($i))"; g test "$i" = 0;' | L_bash_profile analyze
PROFILING: 'f eval "(($i))"; g test "$i" = 0;' to /dev/stdout
PROFING ENDED, output in /dev/stdout
Top 6 cummulatively longest commands:
  percent    spent_us  cmd               calls    spentPerCall  topCaller1    topCaller2    topCaller3    example
---------  ----------  --------------  -------  --------------  ------------  ------------  ------------  -------------
31.4807        14_121  'test 1 = 0'        500          28.242  g 500                                     environment:6
18.8269         8_445  '(( 1 ))'           500          16.89   f 500                                     environment:6
17.2329         7_730  'f eval ((1))'      500          15.46   > 500                                     <:7
16.689          7_486  'g test 1 = 0'      500          14.972  > 500                                     <:7
15.6701         7_029  'eval ((1))'        500          14.058  f 500                                     environment:6
 0.100321          45  'i=1'                 1          45      > 1                                       <:6

Top 6 cummulatively longest commands per call:
  percent    spent_us  cmd               calls    spentPerCall  topCaller1    topCaller2    topCaller3    example
---------  ----------  --------------  -------  --------------  ------------  ------------  ------------  -------------
 0.100321          45  'i=1'                 1          45      > 1                                       <:6
31.4807        14_121  'test 1 = 0'        500          28.242  g 500                                     environment:6
18.8269         8_445  '(( 1 ))'           500          16.89   f 500                                     environment:6
17.2329         7_730  'f eval ((1))'      500          15.46   > 500                                     <:7
16.689          7_486  'g test 1 = 0'      500          14.972  > 500                                     <:7
15.6701         7_029  'eval ((1))'        500          14.058  f 500                                     environment:6

Top 2 cummulatively longest functions:
  percent    spent_us  funcname      calls    spentPerCall    instructions    instructionsPerCall  location
---------  ----------  ----------  -------  --------------  --------------  ---------------------  -------------
  34.4971      15_474  f               500          30.948            1000                      2  environment:6
  31.4807      14_121  g               500          28.242             500                      1  environment:6

Top 2 cummulatively longest functions per call:
  percent    spent_us  funcname      calls    spentPerCall    instructions    instructionsPerCall  location
---------  ----------  ----------  -------  --------------  --------------  ---------------------  -------------
  34.4971      15_474  f               500          30.948            1000                      2  environment:6
  31.4807      14_121  g               500          28.242             500                      1  environment:6

Script executed in 0:00:00.044856us, 2501 instructions, 2 functions.

LICENSE

Licensed under GPLv3. Written by Kamil Cukrowski 2024.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

l_bash_profile-0.1.4.tar.gz (61.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

L_bash_profile-0.1.4-py3-none-any.whl (42.7 kB view details)

Uploaded Python 3

File details

Details for the file l_bash_profile-0.1.4.tar.gz.

File metadata

  • Download URL: l_bash_profile-0.1.4.tar.gz
  • Upload date:
  • Size: 61.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.0.1 CPython/3.12.8

File hashes

Hashes for l_bash_profile-0.1.4.tar.gz
Algorithm Hash digest
SHA256 6175c199e0f8b4e0dee4b4a8ce4f70c28bd3e08e4745769d3383176b13f36aa4
MD5 cc7288f4ebe777f459a1c2235295277e
BLAKE2b-256 62478a88ec64c5effbad5fa3a5e3b95e0b3f007f7c7beae07bb1c4e0a3427df0

See more details on using hashes here.

Provenance

The following attestation bundles were made for l_bash_profile-0.1.4.tar.gz:

Publisher: workflow.yml on Kamilcuk/L_bash_profile

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file L_bash_profile-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: L_bash_profile-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 42.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.0.1 CPython/3.12.8

File hashes

Hashes for L_bash_profile-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 ff67a1d86d29be2ca00120d8521a7b39195a3aed51c7e9cb9609350dc2f9d589
MD5 0bee5af2a11a0668dda469942c50b67a
BLAKE2b-256 34e1ac35b9afdbcf2b2b2fa4cc7b9e4e381f33373eafc1440daeaa29c837e3fb

See more details on using hashes here.

Provenance

The following attestation bundles were made for L_bash_profile-0.1.4-py3-none-any.whl:

Publisher: workflow.yml on Kamilcuk/L_bash_profile

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page