Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
d41c6d9
Update timep.c
jkool702 Sep 19, 2025
9b0c9cf
Update timep.c
jkool702 Sep 19, 2025
fc5fd83
Update timep.c
jkool702 Sep 19, 2025
bd4f0fc
Update timep.c
jkool702 Sep 19, 2025
edf9a55
Update timep.c
jkool702 Sep 19, 2025
9584d9b
Update timep.c
jkool702 Sep 19, 2025
25340bd
Update timep.c
jkool702 Sep 19, 2025
fd94790
Update timep.c
jkool702 Sep 19, 2025
4f2cb3a
Update timep.c
jkool702 Sep 19, 2025
9094099
Update timep.c
jkool702 Sep 19, 2025
b4da4e9
Update build-multiplatform.yml
jkool702 Sep 20, 2025
ac58032
Update build-multiplatform.yml
jkool702 Sep 20, 2025
34d4fcd
Update timep.c
jkool702 Sep 20, 2025
4bf8e5d
Update build-multiplatform.yml
jkool702 Sep 20, 2025
29e0572
Update build-multiplatform.yml
jkool702 Sep 20, 2025
6506759
Update timep.c
jkool702 Sep 20, 2025
5f507d7
Update timep.c
jkool702 Sep 20, 2025
b48ff2e
Update timep.c
jkool702 Sep 20, 2025
39c82d9
Update timep.c
jkool702 Sep 20, 2025
77eba89
Update timep.c
jkool702 Sep 20, 2025
2c41494
Update timep.c
jkool702 Sep 20, 2025
eb52322
Update timep.c
jkool702 Sep 20, 2025
2b575f8
Update build-multiplatform.yml
jkool702 Sep 20, 2025
cadedbe
Update build-multiplatform.yml
jkool702 Sep 20, 2025
d2c6268
Update timep.c
jkool702 Sep 20, 2025
1d8e9bb
Update timep.c
jkool702 Sep 20, 2025
94f45c0
Update timep.c
jkool702 Sep 20, 2025
2d449d7
Update timep.c
jkool702 Sep 20, 2025
75f84e8
Update timep.c
jkool702 Sep 20, 2025
57f295c
Update timep.c
jkool702 Sep 20, 2025
e5946ab
Update timep.so for all architectures and refresh base64 in timep.bash
jkool702 Sep 20, 2025
3cfe3ab
Merge pull request #64 from jkool702/update/timep-binaries-timep_test…
jkool702 Sep 20, 2025
a3a9698
Update timep.c
jkool702 Sep 20, 2025
a40a9e8
Update timep.c
jkool702 Sep 20, 2025
943367c
Update timep.so for all architectures and refresh base64 in timep.bash
jkool702 Sep 20, 2025
a020115
Merge pull request #65 from jkool702/update/timep-binaries-timep_test…
jkool702 Sep 20, 2025
5b5fbbe
Update META
jkool702 Sep 20, 2025
a4c7ab7
Update CHANGELOG.md
jkool702 Sep 20, 2025
6048f41
Update README.md
jkool702 Sep 20, 2025
9e3da66
Update README.md
jkool702 Sep 20, 2025
81d057b
Update CHANGELOG.md
jkool702 Sep 20, 2025
488249c
Merge pull request #62 from jkool702/timep_testing_newLoadables
jkool702 Sep 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 35 additions & 8 deletions .github/workflows/build-multiplatform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ jobs:
platform: linux/riscv64
docker_image: fedorariscv/base:latest
gcc_flags: ""
# - arch: i686
# platform: linux/i686
# docker_image: nickcis/fedora-32:latest
# gcc_flags: ""
steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand All @@ -66,11 +62,42 @@ jobs:
gcc -Wall -fPIC -flto -O3 ${{ matrix.gcc_flags }} -v -DSHELL -DLOADABLE_BUILTIN -DHAVE_CONFIG_H -DSELECT_COMMAND \
-I/usr/include -I/usr/include/bash -I/usr/include/bash/builtins -I/usr/include/bash/include \
-shared -o LIB/LOADABLES/BIN/${{ matrix.arch }}/timep.so timep.c && \
enable -f LIB/LOADABLES/BIN/${{ matrix.arch }}/timep.so getCPUtime timep_crc32 timep_fnv1a; \
enable -f LIB/LOADABLES/BIN/${{ matrix.arch }}/timep.so getCPUtime timep_crc32 timep_fnv1a timep_hash; \
getCPUtime | grep -q -E '^[0-9]+$' || echo \"::warning title=timep.so (${{ matrix.arch }}) getCPUtime test failed::The test for the getCPUtime loadable for the ${{ matrix.arch }} timep.so failed! Please investigate.\"; \
timep_crc32 <<<\"\$RANDOM\" | grep -q -E '^[0-9a-fA-F]+$' || echo \"::warning title=timep.so (${{ matrix.arch }}) timep_crc32 test failed::The test for the timep_crc32 loadable for the ${{ matrix.arch }} timep.so failed! Please investigate.\"; \
timep_fnv1a <<<\"\$RANDOM\" | grep -q -E '^[0-9a-fA-F]+$' || echo \"::warning title=timep.so (${{ matrix.arch }}) timep_fnv1a test failed::The test for the timep_fnv1a loadable for the ${{ matrix.arch }} timep.so failed! Please investigate.\"
"
timep_crc32 <<<\"$RANDOM\" | grep -q -E '^[0-9a-fA-F]+$' || echo \"::warning title=timep.so (${{ matrix.arch }}) timep_crc32 test failed::The test for the timep_crc32 loadable for arch=${{ matrix.arch }} timep.so failed! Please investigate.\"; \
unset val a0 a A AA; \
declare -A AA; \
val=\"${RANDOM}\"; \
a0=\"$(timep_crc32 <<<\"${val}\")\"; \
timep_crc32 - a <<<\"${val}\"; \
timep_crc32 - 'A[1]' <<<\"${val}\"; \
timep_crc32 - 'AA[foo]' <<<\"${val}\"; \
[[ \"${a0}\" == \"${a}\" ]] || echo \"::warning title=timep.so (${{ matrix.arch }}) timep_crc32 - the 'bind to standard variable' test failed::One of the bind-to-variable tests for the timep_crc32 loadable for arch=${{ matrix.arch }} timep.so failed! Please investigate.\"; \
[[ \"${a0}\" == \"${A[1]}\" ]] || echo \"::warning title=timep.so (${{ matrix.arch }}) timep_crc32 - the 'bind to indexed array' test failed::One of the bind-to-variable tests for the timep_crc32 loadable for arch=${{ matrix.arch }} timep.so failed! Please investigate.\"; \
[[ \"${a0}\" == \"${AA[foo]}\" ]] || echo \"::warning title=timep.so (${{ matrix.arch }}) timep_crc32 - the 'bind to associative array' test failed::One of the bind-to-variable tests for the timep_crc32 loadable for arch=${{ matrix.arch }} timep.so failed! Please investigate.\";
timep_fnv1a <<<\"$RANDOM\" | grep -q -E '^[0-9a-fA-F]+$' || echo \"::warning title=timep.so (${{ matrix.arch }}) timep_fnv1a test failed::The test for the timep_fnv1a loadable for arch=${{ matrix.arch }} timep.so failed! Please investigate.\"; \
unset val a0 a A AA; \
declare -A AA; \
val=\"${RANDOM}\"; \
a0=\"$(timep_fnv1a <<<\"${val}\")\"; \
timep_fnv1a - a <<<\"${val}\"; \
timep_fnv1a - 'A[1]' <<<\"${val}\"; \
timep_fnv1a - 'AA[foo]' <<<\"${val}\"; \
[[ \"${a0}\" == \"${a}\" ]] || echo \"::warning title=timep.so (${{ matrix.arch }}) timep_fnv1a - the 'bind to standard variable' test failed::One of the bind-to-variable tests for the timep_fnv1a loadable for arch=${{ matrix.arch }} timep.so failed! Please investigate.\"; \
[[ \"${a0}\" == \"${A[1]}\" ]] || echo \"::warning title=timep.so (${{ matrix.arch }}) timep_fnv1a - the 'bind to indexed array' test failed::One of the bind-to-variable tests for the timep_fnv1a loadable for arch=${{ matrix.arch }} timep.so failed! Please investigate.\"; \
[[ \"${a0}\" == \"${AA[foo]}\" ]] || echo \"::warning title=timep.so (${{ matrix.arch }}) timep_fnv1a - the 'bind to associative array' test failed::One of the bind-to-variable tests for the timep_fnv1a loadable for arch=${{ matrix.arch }} timep.so failed! Please investigate.\";
timep_hash <<<\"$RANDOM\" | grep -q -E '^[0-9a-fA-F]+\-[0-9a-fA-F]+$' || echo \"::warning title=timep.so (${{ matrix.arch }}) timep_hash test failed::The test for the timep_hash loadable for arch=${{ matrix.arch }} timep.so failed! Please investigate.\"; \
unset val a0 a A AA; \
declare -A AA; \
val=\"${RANDOM}\"; \
a0=\"$(timep_hash <<<\"${val}\")\"; \
timep_hash - a <<<\"${val}\"; \
timep_hash - 'A[1]' <<<\"${val}\"; \
timep_hash - 'AA[foo]' <<<\"${val}\"; \
[[ \"${a0}\" == \"${a}\" ]] || echo \"::warning title=timep.so (${{ matrix.arch }}) timep_hash - the 'bind to standard variable' test failed::One of the bind-to-variable tests for the timep_hash loadable for arch=${{ matrix.arch }} timep.so failed! Please investigate.\"; \
[[ \"${a0}\" == \"${A[1]}\" ]] || echo \"::warning title=timep.so (${{ matrix.arch }}) timep_hash - the 'bind to indexed array' test failed::One of the bind-to-variable tests for the timep_hash loadable for arch=${{ matrix.arch }} timep.so failed! Please investigate.\"; \
[[ \"${a0}\" == \"${AA[foo]}\" ]] || echo \"::warning title=timep.so (${{ matrix.arch }}) timep_hash - the 'bind to associative array' test failed::One of the bind-to-variable tests for the timep_hash loadable for arch=${{ matrix.arch }} timep.so failed! Please investigate.\";
"

- name: Upload timep.so artifact
uses: actions/upload-artifact@v4
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
CURRENT VERSION: timep v1.6
CURRENT VERSION: timep v1.6.1

timep v1.6: The changes in this release are largely geared towards various bug fixes and ensuring timep works correctly in more situations. this includes:
* line numbers for subshells inside of functions are now correct
* timep now works if the code being profiled uses `set -e` or `set -u`
* timep now enforces `set -T` like it does `set -m` - if it is disabled timep automatically re-enables it
* there is now a mechanism (which can be disabled via an environment variable) which will cause orphaned processes to automatically exit after the main timep profiling run finishes
* trap handler events are dealt with more robustly
* timep v1.6.1: loadables have been recompiled so that they can bind output to array variables or standard variables.

timep v1.5: 2 major changes are present in this version:
* fixes an issue where it was possible (albiet unlikely) that two sub-trees could be combined in the "combined" profile that shouldnt be. This fix involves computing hashs of all the command strings. To do this without sacraficing performance two new loadable builtins were added - timep_crc32 and timep_fnv1a. To ensure that the (now longer) loadable base64 strings didnt cause the environment size to exceed ARG_MAX, the way that the base64-embedded strings are stored and extracted was re-workedso that the base64 strings are never in a function.
Expand Down
Binary file modified LIB/LOADABLES/BIN/aarch64/timep.so
Binary file not shown.
Binary file modified LIB/LOADABLES/BIN/armv7/timep.so
Binary file not shown.
Binary file modified LIB/LOADABLES/BIN/ppc64le/timep.so
Binary file not shown.
Binary file modified LIB/LOADABLES/BIN/riscv64/timep.so
Binary file not shown.
Binary file modified LIB/LOADABLES/BIN/s390x/timep.so
Binary file not shown.
Binary file modified LIB/LOADABLES/BIN/x86_64/timep.so
Binary file not shown.
111 changes: 103 additions & 8 deletions LIB/LOADABLES/SRC/timep.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,15 @@
#include <arm_acle.h>
#endif

#ifdef HAVE_CONFIG_H
#include <config.h>
/* Prefer Bash's own config.h if available */
#if defined(HAVE_CONFIG_H)
# include <config.h>
#elif __has_include("config.h")
/* Fallback: try local copy in same dir as this source file */
# include "config.h"
#else
/* No config.h available — define minimal defaults if needed */
# warning "No config.h found. Some features may be disabled."
#endif

// Bash internal headers
Expand All @@ -52,6 +59,10 @@
#include "common.h"
#include "xmalloc.h"
#include "variables.h"
#include "array.h"
#include "arrayfunc.h"
#include "assoc.h"
#include "bashtypes.h"

// Helpers for builtins
extern int add_builtin(struct builtin *bp, int keep);
Expand All @@ -64,6 +75,91 @@ static int timep_crc32_main(int argc, char **argv);
static int timep_fnv1a_main(int argc, char **argv);
static int timep_hash_main(int argc, char **argv);

/*
* bind_var_or_array:
*
* Assign a value to a Bash variable, handling both scalars and arrays.
*
* Behavior:
* 1. Scalar variable (e.g., "foo"):
* - Calls bind_variable(varname, value, 0)
* - Creates the variable if it does not exist
*
* 2. Indexed array (e.g., "arr[3]"):
* - Splits into base name and index
* - If the array does not exist, auto-creates an indexed array
* - Calls bind_array_variable() for the assignment
* - Bash will throw "bad array subscript" if the index is invalid (non-numeric)
*
* 3. Associative array (e.g., "A[foo]") that was declared with `declare -A A`:
* - find_variable() returns a valid SHELL_VAR* even if the array is empty
* - bind_assoc_variable() inserts the element into the empty associative array
* - must have previously declared array as associative with "declare -A"
*/
SHELL_VAR *bind_var_or_array(char *name, char *value, int flags) {
if (!name)
return NULL;

char *lb = strchr(name, '[');
if (!lb || name[strlen(name) - 1] != ']') {
/* Simple scalar -> bind_variable. */
char *val_s = savestring(value);
return bind_variable(name, val_s, flags);
}

/* Split base and index; temporary xmalloc buffers */
size_t base_len = (size_t)(lb - name);
char *base_tmp = (char *) xmalloc(base_len + 1);
memcpy(base_tmp, name, base_len);
base_tmp[base_len] = '\0';

size_t idx_len = strlen(lb + 1) - 1; /* drop trailing ']' */
char *idx_tmp = (char *) xmalloc(idx_len + 1);
memcpy(idx_tmp, lb + 1, idx_len);
idx_tmp[idx_len] = '\0';

/* Bash-owned savestring copies */
char *base_s = savestring(base_tmp);
char *idx_s = savestring(idx_tmp);
char *val_s = savestring(value);

xfree(base_tmp);
xfree(idx_tmp);

/* Look up variable */
SHELL_VAR *var = find_variable(base_s);
if (!var) {
/* Variable doesn't exist → create indexed array by default */
var = make_new_array_variable(base_s);
if (!var) {
builtin_error("failed to create array: %s", base_s);
return NULL;
}
}

/* Associative array binding */
if (assoc_p(var)) {
/* Bash expects: bind_assoc_variable(SHELL_VAR *entry, const char *name, char *key, const char *value, int flags) */
return bind_assoc_variable(var, base_s, idx_s, val_s, flags);
}

/* Indexed array binding */
if (array_p(var)) {
char *endp = NULL;
errno = 0;
long n = strtol(idx_s, &endp, 10);
if (endp == idx_s || *endp != '\0' || errno == ERANGE) {
builtin_error("invalid numeric index for indexed array: %s", idx_s);
return NULL;
}
return bind_array_variable(base_s, (arrayind_t)n, val_s, flags);
}

/* Variable exists but is neither indexed nor associative */
builtin_error("%s: not an array", base_s);
return NULL;
}

/* ----------------------------- */
/* -------- getCPUtime -------- */
/* ----------------------------- */
Expand Down Expand Up @@ -150,12 +246,12 @@ static int getCPUtime_main(int argc, char **argv) {
if (var_combined) {
char buf_combined[64];
snprintf(buf_combined, sizeof(buf_combined), "%lld", (long long)micros_combined);
bind_variable(var_combined, buf_combined, 0);
bind_var_or_array(var_combined, buf_combined, 0);

if (var_self) {
char buf_self[64];
snprintf(buf_self, sizeof(buf_self), "%lld", (long long)micros_self);
bind_variable(var_self, buf_self, 0);
bind_var_or_array(var_self, buf_self, 0);
}
} else {
// No variables provided: print combined time to stdout
Expand Down Expand Up @@ -478,7 +574,7 @@ static int timep_crc32_main(int argc, char **argv)
snprintf(outbuf, sizeof(outbuf), "%08x", (unsigned int)crc);

if (varname)
bind_variable((char *)varname, outbuf, 0);
bind_var_or_array((char *)varname, outbuf, 0);
else
printf("%s\n", outbuf);

Expand Down Expand Up @@ -521,7 +617,7 @@ static int timep_fnv1a_main(int argc, char **argv)
snprintf(outbuf, sizeof(outbuf), "%016llx", (unsigned long long)fnv);

if (varname)
bind_variable((char *)varname, outbuf, 0);
bind_var_or_array((char *)varname, outbuf, 0);
else
printf("%s\n", outbuf);

Expand Down Expand Up @@ -564,7 +660,7 @@ static int timep_hash_main(int argc, char **argv)
snprintf(outbuf, sizeof(outbuf), "%08x-%016llx", (unsigned int)crc, (unsigned long long)fnv);

if (varname)
bind_variable((char *)varname, outbuf, 0);
bind_var_or_array((char *)varname, outbuf, 0);
else
printf("%s\n", outbuf);

Expand Down Expand Up @@ -618,4 +714,3 @@ int setup_builtin_timep(void) {

return EXECUTION_SUCCESS;
}

2 changes: 1 addition & 1 deletion META
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
NAME: timep
VERSION: 1.6
VERSION: 1.6.1
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# timep
`timep` is an efficient and state-of-the-art trap-based **time p**rofiler for bash code. `timep` generates a per-command execution time profile for the bash code being profiled. As it generates this profile, `timep` logs command runtimes+metadata hierarchically based on both function and subshell nesting depth, mapping and recreating the complete full call-stack tree for the bash code being profiled.

**CURRENT TIMEP VERSION**: 1.6
**CURRENT TIMEP VERSION**: 1.6.1

**CHANGES IN MOST RECENT UPDATE**: The changes in this release are largely geared towards various bug fixes and ensuring timep works correctly in more situations. this includes:
* line numbers for subshells inside of functions are now correct
Expand All @@ -10,6 +10,8 @@
* there is now a mechanism (which can be disabled via an environment variable) which will cause orphaned processes to automatically exit after the main timep profiling run finishes
* trap handler events are dealt with more robustly

timep v1.6.1: loadables have been recompiled so that they can bind output to array variables or standard variables.

See `CHANGELOG.md` for the changes introduced in previous `timep` updates. To use one of the older versions of timep, download its release or use it via its tag.

-------------------------------------------------------------------------------------------------------------------------------------------
Expand Down
Loading
Loading