From 9b8318ef2cac0b8749f33297462f1744e6169f4e Mon Sep 17 00:00:00 2001 From: Jordan Rome Date: Mon, 21 Sep 2026 16:11:36 -0700 Subject: [PATCH] Update 0.27 docs --- src/pages/docs/release_027/cli.js | 62 ++- src/pages/docs/release_027/language.md | 647 ++++++++++++++++++------- src/pages/docs/release_027/stdlib.md | 566 +++++++++++++++++++-- 3 files changed, 1045 insertions(+), 230 deletions(-) diff --git a/src/pages/docs/release_027/cli.js b/src/pages/docs/release_027/cli.js index 635ba5e..4cb9c7c 100644 --- a/src/pages/docs/release_027/cli.js +++ b/src/pages/docs/release_027/cli.js @@ -94,7 +94,7 @@ tracing capabilities.

-B MODE

-

Set the buffer mode for stdout.

+

Set the output buffer mode (applies to terminal and file output -o).

@@ -131,6 +131,21 @@ For more details see the Debug Output section.

+

--debuginfo DIR[:DIR]

+
+

Add the directory DIR to the search path for DWARF debug information. +Paths may be absolute or relative to the traced binary’s location. +Specify multiple paths either by separating them with a colon (:) or by repeating the option, +e.g. --debuginfo=/bin/debug:./lib/debug:.. and --debuginfo=/bin/debug --debuginfo=./lib/debug, respectively. +Debug files in these paths are matched by build ID when available; prefix a path with + to enable CRC32 validation when needed. +Files that do not match are skipped. +By default, bpftrace searches under standard debug paths, including .debug relative to the traced binary and /usr/lib/debug; it may also query available debuginfod servers.

+
+
+

Split debuginfo format (.dwo, .dwp) is not yet supported for this option, but continues to work with the default probe binary path.

+
+
+

--dry-run

Terminate execution right after attaching all the probes. Useful for testing @@ -163,6 +178,12 @@ it.

+

--fmt FILENAME

+
+

Output standard format for the bpftrace file FILENAME.

+
+
+

-h, --help

Print the help summary.

@@ -236,6 +257,24 @@ For more details see the Listing Probes section.<
+

--probe-filter REGEX

+
+

Only attach and run probes whose name matches the regular expression REGEX. +Probes that do not match are not loaded into the kernel. +Special probes (BEGIN and END) are not affected by this filter. +If no probes match, bpftrace exits with an error.

+
+
+
+

--traceable-functions FILENAME

+
+

Specify the file containing the list of traceable kernel functions. If not set, +bpftrace uses /sys/kernel/tracing/available_filter_functions, which depends on +dynamic ftrace. The format is the same as in available_filter_functions: each +line is either "symbol" or "symbol [module]".

+
+
+

-o FILENAME

Write bpftrace tracing output to FILENAME instead of stdout. @@ -248,7 +287,7 @@ Errors are still written to stderr.

Attach to the process with or filter actions by PID. If the process terminates, bpftrace will also terminate. -When using USDT, uprobes, uretprobes, hardware, software, profile, interval, watchpoint, or asyncwatchpoint probes they will be attached to only this process. +When using USDT, uprobes, uretprobes, hardware, software, profile, interval, or watchpoint probes they will be attached to only this process. For all other probes, except begin/end, the pid will act like a predicate to filter out events not from that pid. For listing uprobes/uretprobes set the target to '*' and the process’s address space will be searched for the symbols.

@@ -265,6 +304,10 @@ For listing uprobes/uretprobes set the target to '*' and the process’s add

Some calls, like 'system', are marked as unsafe as they can have dangerous side effects ('system("rm -rf")') and are disabled by default. This flag allows their use.

+
+

In addition, it makes kprobe/kretprobe checks less restrictive. +It can be used to probe functions that bpftrace reports as not traceable, but are supported if the kernel is configured to allow probing notrace functions.

+

--usdt-file-activation

@@ -329,6 +372,7 @@ Read about how to access positional and named parameters Full list of probe types.

@@ -367,16 +411,6 @@ See src/btf.cpp for the details.

-

BPFTRACE_DEBUG_OUTPUT

-
-

Default: 0

-
-
-

Outputs bpftrace’s runtime debug messages to the trace_pipe. This feature can be turned on by setting -the value of this environment variable to 1.

-
-
-

BPFTRACE_KERNEL_BUILD

Default: /lib/modules/$(uname -r)

@@ -513,7 +547,7 @@ uprobe:/bin/bash:rl_set_prompt const char *prompt # bpftrace -lv 'struct css_task_iter' -struct css_task_iter { +vmlinux: struct css_task_iter { struct cgroup_subsys *ss; unsigned int flags; struct list_head *cset_pos; @@ -563,7 +597,7 @@ Headers are included in the order they are defined, and they are included before
{`# bpftrace --include linux/path.h --include linux/dcache.h \
-    -e 'kprobe:vfs_open { printf("open path: %s\\n", str(((struct path *)arg0)->dentry->d_name.name)); }'
+    -e 'kprobe:vfs_open { printf("open path: %s\\n", str(((struct path *)arg0).dentry.d_name.name)); }'
 
 Attached 1 probe
 open path: .com.google.Chrome.ASsbu2
diff --git a/src/pages/docs/release_027/language.md b/src/pages/docs/release_027/language.md
index 614e480..3bda272 100644
--- a/src/pages/docs/release_027/language.md
+++ b/src/pages/docs/release_027/language.md
@@ -1,13 +1,36 @@
 # The bpftrace Language (0.27)
 
-The `bpftrace` (`bt`) language is inspired by the D language used by `dtrace` and uses the same program structure.
-Each script consists of a [Preamble](#preamble) and one or more [Action Blocks](#action-blocks).
+The `bpftrace` (`bt`) language is inspired by the D language used by `dtrace` and uses a similar program structure.
+Each section is optional but must appear in this order:
+
+1. **[C Definitions](#structs)** — `#include` directives, `#define` macros, and
+   `struct`/`union`/`enum` type definitions. Must come before everything else
+   (aside from a shebang line).
+2. **[Config Block](#config-block) and [Imports](#imports)** — a `config` block and any `import` statements.
+   These can appear in any order relative to each other, but must appear after
+   C definitions and before action blocks, map declarations, and macros.
+3. **[Action Blocks](#action-blocks), [Macros](#macros), and [Map Declarations](#map-declarations)** — the main body of the
+   script. These can appear in any order relative to each other.
+
+For example:
 
 ```
-preamble
+#include 
+#define RED "\033[31m"
 
-actionblock1
-actionblock2
+struct S {
+  int x;
+}
+
+config = {
+    stack_mode=perf
+}
+
+let @a = lruhash(100);
+
+macro greet { print("hi"); }
+
+kprobe:do_nanosleep { greet!(); }
 ```
 
 ## Action Blocks
@@ -15,14 +38,14 @@ actionblock2
 Each action block consists of three parts:
 
 ```
-probe[,probe]
+[name=]probe[,probe]
 /predicate/ {
   action
 }
 ```
 
 * **Probes**\
-  A probe specifies the event and event type to attach to. [Probes list](#probes).
+  A probe specifies the event and event type to attach to. See [the probes section](#probes) for more detail.
 * **Predicate**\
   The predicate is an optional condition that must be met for the action to be executed.
 * **Action**\
@@ -97,7 +120,7 @@ struct MyStruct {
 
 kprobe:dummy {
   $s = (struct MyStruct *) arg0;
-  print($s->y[0]);
+  print($s.y[0]);
 }
 ```
 
@@ -196,7 +219,7 @@ if (condition) {
 ## Config Block
 
 To improve script portability, you can set bpftrace [Config Variables](#config-variables) via the config block,
-which can only be placed at the top of the script (in the [preamble](#preamble)) before any action blocks.
+which can only be placed at the top of the script before any action blocks, macros, or map declarations.
 
 ```
 config = {
@@ -221,7 +244,7 @@ inside a script config block.
 ## Config Variables
 
 Some behavior can only be controlled through config variables, which are listed here.
-These can be set via the [Config Block](#config-block) directly in a script (before any probes) or via their environment variable equivalent, which is upper case and includes the `BPFTRACE_` prefix e.g. ``stack_mode`’s environment variable would be `BPFTRACE_STACK_MODE`.
+These can be set via the [Config Block](#config-block) directly in a script (before any probes) or via their environment variable equivalent, which is upper case and includes the `BPFTRACE_` prefix e.g. `stack_mode`’s environment variable would be `BPFTRACE_STACK_MODE`.
 
 ### cache_user_symbols
 
@@ -250,7 +273,15 @@ For user space symbols, symbolicate lazily/on-demand (`true`) or symbolicate eve
 
 Default: "GPL"
 
-The license bpftrace will use to load BPF programs into the linux kernel.
+The license bpftrace will use to load BPF programs into the linux kernel. Here is the list of accepted license strings:
+- GPL
+- GPL v2
+- GPL and additional rights
+- Dual BSD/GPL
+- Dual MIT/GPL
+- Dual MPL/GPL
+
+[Read More about BPF licenses](#bpf-license)
 
 ### log_size
 
@@ -318,13 +349,15 @@ This exists because the BPF stack is limited to 512 bytes and large objects make
 
 ### perf_rb_pages
 
-Default: 64
+Default: Based on available system memory
 
-Number of pages to allocate per CPU perf ring buffer.
-The value must be a power of 2.
-If you’re getting a lot of dropped events bpftrace may not be processing events in the ring buffer fast enough.
+Number of pages to allocate for each created ring or perf buffer (there is only one of each max).
+The minimum is: 1 * the number of cpus on your machine.
+If you’re getting a lot of dropped events bpftrace may not be processing events in the ring buffer (or perf buffer if you're using `skboutput`) fast enough.
 It may be useful to bump the value higher so more events can be queued up.
 The tradeoff is that bpftrace will use more memory.
+The default value is based on available system memory; max is 4096 pages (16mb) and min is 64 pages (256kb), which presumes 4k page size.
+If your system has a larger page size the amount of allocated memory will be the same but we'll just use fewer pages.
 
 ### show_debug_info
 
@@ -338,12 +371,15 @@ Default: bpftrace
 Output format for ustack and kstack builtins.
 Available modes/formats:
 
-* bpftrace
-* perf
-* raw: no symbolication
+* bpftrace: symbol + offset (e.g. `do_mmap+1`)
+* perf: linux perf style with leading IP (e.g. `ffffffffb4019501 do_mmap+1`)
+* raw: no symbolication (print instruction pointer)
+* build_id: no symbolication (print build_id and file offset) (ustack only)
 
 This can be overwritten at the call site.
 
+When [debug info](#show_debug_info) is available the file and line is added at the end for `bpftrace` or `perf` stack mode e.g. `spin+37@/home/jordalgo/local/bpftrace/tests/testprogs/uprobe_loop.c:14`.
+
 ### str_trunc_trailer
 
 Default: `..`
@@ -360,10 +396,8 @@ Controls whether maps are printed on exit. Set to `false` in order to change the
 ### unstable features
 
 These are the list of unstable features:
-- `unstable_macro` -  feature flag for bpftrace macros
-- `unstable_map_decl` - feature flag for map declarations
 - `unstable_tseries` - feature flag for time series map type
-- `unstable_addr` - feature flag for address of operator (&)
+- `unstable_dw_ustack` - feature flag for DWARF-based user-space stack unwinding
 
 All of these accept the following options:
 
@@ -376,9 +410,6 @@ Default: warn
 ## Data Types
 
 The following fundamental types are provided by the language.
-Note: Integers are by default represented as 64 bit signed but that can be
-changed by either casting them or, for scratch variables, explicitly specifying
-the type upon declaration.
 
 |     |     |
 | --- | --- |
@@ -392,6 +423,7 @@ the type upon declaration.
 | int32 | Signed 32 bit integer |
 | uint64 | Unsigned 64 bit integer |
 | int64 | Signed 64 bit integer |
+| string | See below |
 
 ```
 begin { $x = 1<<16; printf("%d %d\n", (uint16)$x, $x); }
@@ -402,6 +434,57 @@ begin { $x = 1<<16; printf("%d %d\n", (uint16)$x, $x); }
  */
 ```
 
+Integers are by default represented as the smallest possible
+signed type, e.g. `0`, `1`, and `-1` are all `int8`.
+If an integer literal exceeds the largest `int64` then it's a `uint64`.
+Positive integer literals are flexible though.
+For this code:
+```
+$a = 1;
+$a = (uint64)2;
+```
+`$a` ends up being a `uint64` as bpftrace can determine this statically.
+
+Scratch variables and map keys/values will be automatically upcast when necessary, e.g.
+```
+$a = 1; // starts as int8
+$b = -1000; // starts as int16
+$a = $b; // $a now becomes an int16
+
+$c = (uint64)1;
+$d = (int64)-1;
+
+// $c is a int64 below
+// an implicit cast is added to the assignment -> (int64)$d
+// and a warning about the sign mismatch is surfaced
+$c = $d;
+```
+
+Note: If there is ever an integer sign mismatch that can't be upcast to a type that can hold both, the resulting type is an `int64`.
+This may lead to undefined behavior, or, most likely, a large number being printed as a negative one.
+
+However, implicit casts of integer literals still fail when the literal is outside the
+destination type's range. For example, `let $a: uint16 = -1;` requires an
+explicit cast: `let $a: uint16 = (uint16)-1;`. Though this:
+```
+$b = -1;
+let $a: uint16 = $b;
+```
+only yields a warning.
+
+Additionally, when vmlinux BTF is available, bpftrace supports
+casting to some of the kernel's fixed integer types:
+```
+$a = (uint64_t)1; // $a is a uint64
+```
+
+### String
+
+bpftrace also supports a `string` data type, which it uses for string literals, e.g. `"hello"`.
+Similar to C this is represented as a well formed char array (NULL terminated).
+Additionally, all BTF char arrays (`char[]` or `int8[]`) are automatically converted to a bpftrace string but can be casted back to an int array if needed, e.g. `$a = (int8[])"mystring"`;
+It also may be necessary to utilize the [`str()`](stdlib#str) function if bpftrace can't determine the correct address space (user or kernel).
+
 ## Filters/Predicates
 
 Filters (also known as predicates) can be added after probe names.
@@ -421,13 +504,85 @@ kprobe:vfs_read /comm == "bash"/ {
 
 Floating-point numbers are not supported by BPF and therefore not by bpftrace.
 
+## Imports
+
+Root-level imports allow you to import other files into your script using the `import` statement.
+
+### Syntax
+
+```
+import "";
+```
+
+Import statements must appear after [C definitions](#structs) but before any [action blocks](#action-blocks), [macros](#macros), or [map declarations](#map-declarations).
+
+### Supported file types
+
+| Extension | Description |
+|-----------|-------------|
+| `.bt` | bpftrace script — probes, macros, and map declarations are merged into the importing script |
+| `.h` | C header — type definitions are made available to the importing script |
+| `.bpf.c` | BPF C source — compiled and linked into the BPF program. These are checked by the BPF verifier. |
+
+If a directory is specified instead of a file, all supported files in that directory (non-recursive) are imported.
+
+### Path resolution
+
+The import path is resolved relative to the directory containing the script that has the `import` statement. For example, if `/home/user/script.bt` contains `import "helpers.bt";`, bpftrace looks for `/home/user/helpers.bt`.
+
+As a security measure, bpftrace refuses to import from world-writable directories.
+
+### Examples
+
+Importing a bpftrace script:
+
+```
+// helpers.bt
+macro greet { print("hello"); }
+```
+
+```
+import "helpers.bt";
+
+begin { greet!(); } // prints "hello"
+```
+
+Importing a C file:
+
+```
+// my_c_lib.bpf.c
+int __add_one(int val) { return 1 + val; }
+```
+
+```
+import "my_c_lib.bpf.c";
+
+begin {
+  print(__add_one(1)); // prints 2
+}
+```
+
+Importing a directory of files:
+
+```
+import "my_lib";
+```
+
+This imports all supported files in the `my_lib/` directory.
+
+### Behavior notes
+
+- Each import path is only imported once. Duplicate imports of the same path are silently ignored.
+- Imported `.bt` scripts can themselves contain `import` statements.
+- An imported `.bt` script cannot contain a `config` block. Only one config block is allowed in the root script.
+
 ## Identifiers
 
 Identifiers must match the following regular expression: `[_a-zA-Z][_a-zA-Z0-9]*`
 
 ## Keywords
 
-`break`, `config`, `continue`, `else`, `for`, `if`, `import`, `let`, `macro`, `offsetof`, `return`, `sizeof`, `unroll`, `while`.
+`break`, `config`, `continue`, `else`, `for`, `if`, `import`, `let`, `macro`, `offsetof`, `return`, `sizeof`, `unroll`, `while` (deprecated).
 
 * `return` - The return keyword is used to exit the current probe. This differs from `exit()` in that it doesn’t exit bpftrace.
 
@@ -544,37 +699,11 @@ Both `for` loops support the following control flow statements:
 | --- | --- |
 | continue | skip processing of the rest of the block and proceed to the next iteration |
 | break | terminate the loop |
+| return | return from the current probe |
 
 ### While
 
-BPF supports `while` loops as long as the verifier can prove they’re bounded and fit within the instruction limit.
-
-```
-while (condition) {
-  block;
-}
-```
-
-```
-interval:s:1 {
-  $i = 0;
-  while ($i <= 100) {
-    printf("%d ", $i);
-    if ($i > 5) {
-      break;
-    }
-    $i++
-  }
-  printf("\n");
-}
-```
-
-The `while` loop supports the following control flow statements:
-
-|     |     |
-| --- | --- |
-| continue | skip processing of the rest of the block and return to the conditional |
-| break | terminate the loop |
+While loops are deprecated and may be removed in the future; please use `For` loops instead as these are more easily verified to be bounded.
 
 ### Unroll
 
@@ -607,9 +736,6 @@ interval:s:1 {
 
 ## Macros
 
-***Warning*** this feature is experimental and may be subject to changes.
-Stabilization is tracked in [#4079](https://github.com/bpftrace/bpftrace/issues/4079).
-
 bpftrace macros (as opposed to C macros) provide a way for you to structure your script.
 They can be useful when you want to factor out code into smaller, more understandable parts.
 Or if you want to share code between probes.
@@ -621,6 +747,8 @@ A macro's parameter signature specifies how an argument will be used.
 For example `macro test($a, b, @c)` indicates that `$a` needs to be a scratch variable (which might be mutated), that `b` needs to be an expression that will be inserted where ever `b` is used in the macro body, and that `@c` needs to be a map (which might be mutated).
 A valid use of this macro could be `test($x, 1 + 2, @y)`.
 Variables and maps can also be used for ident parameters that expect expressions and would be the same as writing `{ @y }` (Block Expression).
+Type expression substitution is also supported inside of macros but types must be passed into macro calls wrapped in the `typeof` builtin (see below).
+Note: User-defined macros with the same name and signature as a standard library macro will override the standard library version. However, standard library macros nested inside of other standard library macros will never use the user-defined version of the same signature.
 
 Here are some valid usages of macros:
 
@@ -648,6 +776,10 @@ macro add_two(x) {
   add_one(x) + 1
 }
 
+macro p_cast(a, b) {
+  (a*)b
+}
+
 begin {
   print(one());                   // prints 1
   print(one);                     // prints 1 (bare identifier works if the macro accepts 0 args)
@@ -663,6 +795,10 @@ begin {
   side_effects({ printf("hi") })  // prints hihihi
 
   print(add_two(1));              // prints 3
+
+  print(
+    p_cast(typeof(uint8), -1)
+  );                              // prints 0xff
 }
 ```
 
@@ -710,14 +846,19 @@ The following operators are available for integer arithmetic:
 
 Operations between a signed and an unsigned integer are allowed providing
 bpftrace can statically prove a safe conversion is possible. If safe conversion
-is not guaranteed, the operation is undefined behavior and a corresponding
-warning will be emitted.
+is not guaranteed, the operation is undefined behavior.
 
 If the two operands are different size, the smaller integer is implicitly
 promoted to the size of the larger one. Sign is preserved in the promotion.
 For example, `(uint32)5 + (uint8)3` is converted to `(uint32)5 + (uint32)3`
 which results in `(uint32)8`.
 
+Subtraction (as well as decrement below) always yields a signed integer type
+as this is equivalent to addition with a signed (negative) integer, e.g.
+these two assignments yield the same type (`int64`):
+`$a = (uint64)1 - (uint64)2; $b = (uint64)1 + (-2)`.
+To maintain an unsigned type, cast the result, e.g. `$a = (uint64)((uint64)1 - (uint64)2);`.
+
 Pointers may be used with arithmetic operators but only for addition and
 subtraction. For subtraction, the pointer must appear on the left side of the
 operator. Pointers may also be used with logical operators; they are considered
@@ -754,7 +895,7 @@ The following relational operators are defined for integers and pointers.
 | == | left-hand expression equal to right-hand |
 | != | left-hand expression not equal to right-hand |
 
-The following relation operators are available for comparing strings and integer arrays.
+The following relation operators are available for comparing strings, integer arrays, and tuples.
 
 |     |     |
 | --- | --- |
@@ -814,36 +955,25 @@ let $a = {
 
 This can be used anywhere an expression can be used.
 
-## Preamble
-
-The preamble consists of multiple optional pieces:
-- preprocessor definitions
-- type definitions
-- a [config block](#config-block)
-- [map declarations](#map-declarations)
-
-For example:
+**Note:** There will be a warning for discarded expressions, e.g.,
 
 ```
-#include 
-#define RED "\033[31m"
-
-struct S {
-  int x;
-}
-
-config = {
-    stack_mode=perf
-}
-
-let @a = lruhash(100);
+{ 1 } // Warning
+$a = { 1 } // No Warning
+has_key(@a, 1); // Warning
+$b = has_key(@a, 1); // No Warning
+```
+The warning can also be silenced by utilizing the Discard Expression:
 
+```
+_ = has_key(@a, 1); // No Warning
 ```
 
 ## Probes
 
 bpftrace supports various probe types which allow the user to attach BPF programs to different types of events.
 Each probe starts with a provider (e.g. `kprobe`) followed by a colon (`:`) separated list of options.
+An optional name may precede the provider with an equals sign (e.g. `name=`), which is reserved for internal use and future features.
 The amount of options and their meaning depend on the provider and are detailed below.
 The valid values for options can depend on the system or binary being traced, e.g. for uprobes it depends on the binary.
 Also see [Listing Probes](cli#listing-probes).
@@ -894,13 +1024,15 @@ Most providers also support a short name which can be used instead of the full n
 | [`tracepoint`](#tracepoint) | `t` | Kernel static tracepoints |
 | [`uprobe/uretprobe`](#uprobe-uretprobe) | `u`/`ur` | User-level function start/return |
 | [`usdt`](#usdt) | `U` | User-level static tracepoints |
-| [`watchpoint/asyncwatchpoint`](#watchpoint-and-asyncwatchpoint) | `w`/`aw` | Memory watchpoints |
+| [`watchpoint`](#watchpoint) | `w` | Memory watchpoints |
 
 ### begin/end
 
 These are special built-in events provided by the bpftrace runtime.
 `begin` is triggered before all other probes are attached.
 `end` is triggered after all other probes are detached.
+Each of these probes can be used any number of times, and they will be executed in the same order they are declared.
+For imports containing `begin` and `end` probes, an effort is made to preserve the partial order implied by the import graph (e.g. if `A` depends on `B`, then `B` will have both its `begin` and `end` probes executed first), but this is not strictly guaranteed.
 
 Note that specifying an `end` probe doesn’t override the printing of 'non-empty' maps at exit.
 To prevent printing all used maps need be cleared in the `end` probe:
@@ -912,15 +1044,30 @@ end {
 }
 ```
 
+### test
+
+`test` is a special built-in probe type for creating tests.
+bpftrace executes each `test` probe and checks the return value, error count and possible exit calls to determine a pass.
+If multiple `test` probes exist in a script, bpftrace executes them sequentially in the order they are specified.
+To run `test` probes, you must run bpftrace in test mode: `bpftrace --test ...`; otherwise `test` probes will be ignored.
+
+```
+test:okay {
+  print("I'm okay! This output will be suppressed.");
+}
+
+test:failure {
+  print("This is a failure! This output will be shown");
+  return 1;
+}
+```
+
 ### bench
 
 `bench` is a special built-in probe type for creating micro benchmarks.
-bpftrace executes each `bench` probe repeatedly to measure the average
-execution time of the contained code. If multiple `bench` probes exist
-in a script, bpftrace executes them sequentially in the order they are
-specified. To run `bench` probes, you must run bpftrace in bench mode:
-`bpftrace --test-mode bench ...`; otherwise, `bench` probes will be
-ignored.
+bpftrace executes each `bench` probe repeatedly to measure the average execution time of the contained code.
+If multiple `bench` probes exist in a script, bpftrace executes them sequentially in the order they are specified.
+To run `bench` probes, you must run bpftrace in bench mode: `bpftrace --bench ...`; otherwise, `bench` probes will be ignored.
 
 ```
 bench:lhist {
@@ -1052,7 +1199,7 @@ ctx pointer. Users can display the set of available fields for each iterator via
 -lv options as described below.
 
 ```
-iter:task { printf("%s:%d\n", ctx->task->comm, ctx->task->pid); }
+iter:task { printf("%s:%d\n", ctx.task.comm, ctx.task.pid); }
 
 /*
  * Sample output:
@@ -1067,7 +1214,7 @@ iter:task { printf("%s:%d\n", ctx->task->comm, ctx->task->pid); }
 
 ```
 iter:task_file {
-  printf("%s:%d %d:%s\n", ctx->task->comm, ctx->task->pid, ctx->fd, path(ctx->file->f_path));
+  printf("%s:%d %d:%s\n", ctx.task.comm, ctx.task.pid, ctx.fd, path(ctx.file.f_path));
 }
 
 /*
@@ -1084,7 +1231,7 @@ iter:task_file {
 
 ```
 iter:task_vma {
-  printf("%s %d %lx-%lx\n", comm, pid, ctx->vma->vm_start, ctx->vma->vm_end);
+  printf("%s %d %lx-%lx\n", comm, pid, ctx.vma.vm_start, ctx.vma.vm_end);
 }
 
 /*
@@ -1101,7 +1248,7 @@ It can be specified as an absolute or relative path to /sys/fs/bpf.
 **relative pin**
 
 ```
-iter:task:list { printf("%s:%d\n", ctx->task->comm, ctx->task->pid); }
+iter:task:list { printf("%s:%d\n", ctx.task.comm, ctx.task.pid); }
 
 /*
  * Sample output:
@@ -1113,7 +1260,7 @@ iter:task:list { printf("%s:%d\n", ctx->task->comm, ctx->task->pid); }
 
 ```
 iter:task_file:/sys/fs/bpf/files {
-  printf("%s:%d %s\n", ctx->task->comm, ctx->task->pid, path(ctx->file->f_path));
+  printf("%s:%d %s\n", ctx.task.comm, ctx.task.pid, path(ctx.file.f_path));
 }
 
 /*
@@ -1171,7 +1318,7 @@ fentry:tcp_reset
 
 ```
 fentry:x86_pmu_stop {
-  printf("pmu %s stop\n", str(args.event->pmu->name));
+  printf("pmu %s stop\n", str(args.event.pmu.name));
 }
 ```
 
@@ -1179,7 +1326,7 @@ The fget function takes one argument as file descriptor and you can access it vi
 
 ```
 fexit:fget {
-  printf("fd %d name %s\n", args.fd, str(retval->f_path.dentry->d_name.name));
+  printf("fd %d name %s\n", args.fd, str(retval.f_path.dentry.d_name.name));
 }
 
 /*
@@ -1195,7 +1342,9 @@ fexit:fget {
 
 * `kprobe[:module]:fn`
 * `kprobe[:module]:fn+offset`
+* `kprobe:addr`
 * `kretprobe[:module]:fn`
+* `kretprobe:addr`
 
 **short names**
 
@@ -1231,7 +1380,7 @@ It is up to the user to perform [Type conversion](#type-conversion) when needed,
 
 kprobe:vfs_open
 {
-	printf("open path: %s\n", str(((struct path *)arg0)->dentry->d_name.name));
+	printf("open path: %s\n", str(((struct path *)arg0).dentry.d_name.name));
 }
 ```
 
@@ -1243,7 +1392,7 @@ If the kernel has BTF (BPF Type Format) data, all kernel structs are always avai
 
 ```
 kprobe:vfs_open {
-  printf("open path: %s\n", str(((struct path *)arg0)->dentry->d_name.name));
+  printf("open path: %s\n", str(((struct path *)arg0).dentry.d_name.name));
 }
 ```
 
@@ -1253,13 +1402,13 @@ You can optionally specify a kernel module, either to include BTF data from that
 kprobe:kvm:x86_emulate_insn
 {
   $ctxt = (struct x86_emulate_ctxt *) arg0;
-  printf("eip = 0x%lx\n", $ctxt->eip);
+  printf("eip = 0x%lx\n", $ctxt.eip);
 }
 ```
 
 See [BTF Support](#btf-support) for more details.
 
-`kprobe` s are not limited to function entry, they can be attached to any instruction in a function by specifying an offset from the start of the function.
+`kprobe` s are not limited to function entry, they can be attached to any instruction in a function by specifying an offset from the start of the function or by providing the bare address of the function, which is useful if there are multiple functions with the same name. The bare address variant `kprobe:addr` requires the `--unsafe` flag.
 
 `kretprobe` s trigger on the return from a kernel function.
 Return probes do not have access to the function (input) arguments, only to the return value (through `retval`).
@@ -1269,7 +1418,7 @@ A common pattern to work around this is by storing the arguments in a map on fun
 kprobe:d_lookup
 {
 	$name = (struct qstr *)arg1;
-	@fname[tid] = $name->name;
+	@fname[tid] = $name.name;
 }
 
 kretprobe:d_lookup
@@ -1415,6 +1564,7 @@ After the "common" members listed first, the members are specific to the tracepo
 * `uprobe:binary:func`
 * `uprobe:binary:func+offset`
 * `uprobe:binary:offset`
+* `uprobe:binary@file:line[:col]`
 * `uretprobe:binary:func`
 
 **short names**
@@ -1459,10 +1609,47 @@ uprobe:/bin/bash:rl_set_prompt
     const char* prompt
 ```
 
-When tracing C++ programs, it’s possible to turn on automatic symbol demangling by using the `:cpp` prefix:
+Using DWARF source code location info, bpftrace can also attach uprobes directly to source file statements, similar to setting a breakpoint at a `file:line[:col]` location in a debugger. This avoids manually locating instruction offsets in the ELF when probes are needed inside a function body.
+
+```
+uprobe:/bin/bash@readline.c:362 { ... }
+```
+
+`file` path may be absolute or relative, and `line:col` must refer to a valid statement in that file. Only statements originating from the specified file are considered; statements from included files are ignored.
+
+When tracing C++ programs, the `cpp` qualifier enables automatic symbol demangling, allowing you to specify function names in their human-readable form instead of the compiler-mangled form.
+
+For example, given this C++ code:
 
+```cpp
+namespace MyApp {
+  class Server {
+  public:
+    void handle_request(int fd) { ... }
+  };
+}
 ```
-# bpftrace:cpp:"bpftrace::BPFtrace::add_probe" { ... }
+
+The compiler mangles the function name to `_ZN5MyApp6Server14handle_requestEi`.
+Instead of using that directly, use the `cpp` qualifier:
+
+```
+# using cpp qualifier with demangled name
+uprobe:/path/to/myapp:cpp:"MyApp::Server::handle_request" { print(ustack); }
+
+# wildcards also work with cpp qualifier
+uprobe:/path/to/myapp:cpp:"MyApp::Server::*" { print(ustack); }
+```
+
+Without the `cpp` qualifier you must use the mangled name directly.
+You can find it using `nm`:
+
+```
+$ nm /path/to/myapp | grep handle_request
+_ZN5MyApp6Server14handle_requestEi
+
+# use the mangled name directly in the probe
+uprobe:/path/to/myapp:"_ZN5MyApp6Server14handle_requestEi" { print(ustack); }
 ```
 
 It is important to note that for `uretprobe` s to work the kernel runs a special helper on user-space function entry which overrides the return address on the stack.
@@ -1493,6 +1680,14 @@ stack: frame={sp:0xc00008cf60, fp:0xc00008cfd0} stack=[0xc00008c000,0xc00008d000
 fatal error: unknown caller pc
 ```
 
+Uprobe targets are expected to be valid ELF binaries. Unsafe mode (`--unsafe`) allows probing arbitrary files containing executable code.
+
+For shared libraries mapped directly from ZIP archives (common on Android), the archive and library name can be separated by `!/`:
+
+```
+uprobe:"/system/app/Foo/Foo.apk!/lib/arm64-v8a/libfoo.so":func { ... }
+```
+
 ### usdt
 
 **variants**
@@ -1540,17 +1735,15 @@ Also note that --usdt-file-activation matches based on file path.
 This means that if bpftrace runs from the root host, things may not work as expected if there are processes execved from private mount namespaces or bind mounted directories.
 One workaround is to run bpftrace inside the appropriate namespaces (i.e. the container).
 
-### watchpoint and asyncwatchpoint
+### watchpoint
 
 **variants**
 
 * `watchpoint:absolute_address:length:mode`
-* `watchpoint:function+argN:length:mode`
 
 **short names**
 
 * `w`
-* `aw`
 
 This feature is experimental and may be subject to interface changes.
 Memory watchpoints are also architecture dependent.
@@ -1559,19 +1752,7 @@ These are memory watchpoints provided by the kernel.
 Whenever a memory address is written to (`w`), read
 from (`r`), or executed (`x`), the kernel can generate an event.
 
-In the first form, an absolute address is monitored.
-If a pid (`-p`) or a command (`-c`) is provided, bpftrace takes the address as a userspace address and monitors the appropriate process.
-If not, bpftrace takes the address as a kernel space address.
-
-In the second form, the address present in `argN` when `function` is entered is
-monitored.
-A pid or command must be provided for this form.
-If synchronous (`watchpoint`), a `SIGSTOP` is sent to the tracee upon function entry.
-The tracee will be ``SIGCONT``ed after the watchpoint is attached.
-This is to ensure events are not missed.
-If you want to avoid the `SIGSTOP` + `SIGCONT` use `asyncwatchpoint`.
-
-Note that on most architectures you may not monitor for execution while monitoring read or write.
+Once the watchpoint is attached, an absolute address is monitored.
 
 ```
 # bpftrace -e 'watchpoint:0x10000000:8:rw { printf("hit!\n"); }' -c ./testprogs/watchpoint
@@ -1585,51 +1766,78 @@ watchpoint:0x$(awk '$3 == "jiffies" {print $1}' /proc/kallsyms):8:w {
 }
 ```
 
-"hit" and exit when the memory pointed to by `arg1` of `increment` is written to:
+## Types
 
-```C
-# cat wpfunc.c
-#include 
-#include 
-#include 
+### Type Syntax
 
-__attribute__((noinline))
-void increment(__attribute__((unused)) int _, int *i)
-{
-  (*i)++;
-}
+bpftrace uses a postfix type syntax for pointer and array modifiers.
+Modifiers are read left-to-right: the base type comes first, followed by
+any combination of `*` (pointer) and `[N]` (array) suffixes in any order.
 
-int main()
-{
-  int *i = malloc(sizeof(int));
-  while (1)
-  {
-    increment(0, i);
-    (*i)++;
-    usleep(1000);
-  }
-}
 ```
+int32*       // pointer to int32
+int32[4]     // array of 4 int32
+int32*[4]    // array of 4 pointers to int32
+int32[4]*    // pointer to an array of 4 int32
+int32*[4]*   // pointer to an array of 4 pointers to int32
+```
+
+This differs from C, where pointer and array declarators are written around
+the variable name and read inside-out. The table below shows equivalent
+types in both syntaxes:
+
+| C syntax | bpftrace syntax | Description |
+| --- | --- | --- |
+| `int *p` | `int32*` | pointer to int |
+| `int a[4]` | `int32[4]` | array of 4 ints |
+| `int *a[4]` | `int32*[4]` | array of 4 pointers to int |
+| `int (*p)[4]` | `int32[4]*` | pointer to array of 4 ints |
+| `struct foo *p` | `struct foo*` | pointer to struct foo |
+| `struct foo *a[4]` | `struct foo*[4]` | array of 4 pointers to struct foo |
+| `struct foo (*p)[4]` | `struct foo[4]*` | pointer to array of 4 struct foo |
+
+The same syntax applies to type annotations in variable declarations, casts, and type introspection functions (e.g. `sizeof`, `offsetof`, etc.)
+
+For the parameterized types `string`, `buffer`, and `inet`, a `[N]` suffix
+sets the type's size parameter rather than creating an array:
 
 ```
-# bpftrace -e 'watchpoint:increment+arg1:4:w { printf("hit!\n"); exit() }' -c ./wpfunc
+$s = (string[64])arg0;    // string with capacity 64 bytes
+$b = (buffer[256])arg0;   // buffer of 256 bytes
 ```
 
-Note that threads are monitored, but only for threads created after watchpoint attachment.
-The is a limitation from the kernel.
-Additionally, because of how watchpoints are implemented in bpftrace the specified function must be called at least once in the main thread in order to observe future calls to this function in child threads.
+### Type Context Builtins
+
+The following functions accept a type OR an expression, which is evaluated in order to get a type:
+- `sizeof`
+- `offsetof`
+- `typeof`
+- `typeinfo` (unstable)
+
+Examples:
+```
+print(sizeof(uint32));               // prints 4 as uint32 is parsed as a type
+print(sizeof({ $a = (int8)1; $a })); // prints 1 as the expression evaluates to $a whose type is int8
+print(sizeof(uint32*[10]));          // prints 80 as the type is an array of 10 pointers to uint32
+```
+
+Note: any expression passed to these functions is removed before actual runtime e.g. in `print(sizeof({ $a = (int8)1; print("hi"); $a }));` the "hi" is never printed and maps and variables are not mutated.
+
+If passing a type to a macro call, you must wrap that type in a `typeof` e.g. `my_macro($a, typeof(struct task))`.
 
-## Pointers
+### Pointers
 
 Pointers in bpftrace are similar to those found in `C`.
+You can also get the pointer to a bpftrace scratch variable or map using the address-of operator (`&`), e.g. `$a = 1; $b = &$a;`.
 
-## Structs
+### Structs
 
 `C` like structs are supported by bpftrace.
 Fields are accessed with the `.` operator.
-Fields of a pointer to a struct can be accessed with the `\->` operator.
+If the `.` is used on a pointer, it is automatically dereferenced.
+The legacy `->` operator may be used, but is purely an alias for the `.` operator.
 
-Custom structs can be defined in the preamble.
+Custom structs can be defined at the top of the program.
 
 Constructing structs from scratch, like `struct X var = {.f1 = 1}` in `C`, is not supported.
 They can only be read into a variable from a pointer.
@@ -1643,43 +1851,61 @@ kprobe:dummy {
   $ptr = (struct MyStruct *) arg0;
   $st = *$ptr;
   print($st.a);
-  print($ptr->a);
+  print($ptr.a);
 }
 ```
 
-## Tuples
+### Tuples
 
-bpftrace has support for immutable N-tuples (`n > 1`).
+bpftrace has support for immutable N-tuples.
 A tuple is a sequence type (like an array) where, unlike an array, every element can have a different type.
 
-Tuples are a comma separated list of expressions, enclosed in brackets, `(1,2)`
-Individual fields can be accessed with the `.` operator.
-Tuples are zero indexed like arrays are.
+Tuples are a comma separated list of expressions, enclosed in parenthesis, `(1,"hello")`.
+Individual fields can be accessed with the `.` operator or via array-style access.
+The array index expression must evaluate to an integer literal at compile time (no variables but this is ok `(1, "hello")[1 - 1]`).
+Tuples are zero indexed like arrays. Examples:
 
 ```
 interval:s:1 {
-  $a = (1,2);
+  $a = (1,"hello");
   $b = (3,4, $a);
-  print($a);
-  print($b);
-  print($b.0);
+  print($a);     // (1, "hello")
+  print($b);     // (3, 4, (1, "hello"))
+  print($b.0);   // 3
+  print($a[1]);  // "hello"
 }
+```
 
-/*
- * Sample output:
- * (1, 2)
- * (3, 4, (1, 2))
- * 3
- */
+Single-element and empty tuples can be specified using Python-like syntax.
+A single element tuple requires a trailing comma, `(1,)`, while the empty tuple is simply `()`.
+
+### Records
+
+bpftrace has support for immutable N-records.
+A record is a struct-like type where every element has a name and a type.
+
+Records are a comma separated list of named expressions, enclosed in parenthesis, `(color="green", size=2)`.
+Individual fields can be accessed with the `.` operator and the field name.
+If records are assigned in a different order to the same variable, map key, or map value then the final ordering is ambiguous. Note that the evaluation order is maintained, e.g. `$a = (size={ print("first"); 20 }, color={ print("second"); "pink" });` will print "first" then "second" regardless of the ordering of the final type.
+Examples:
+
+```
+interval:s:1 {
+  $a = (color="green", size=2);
+  print($a);        // { .color = "green", .size = 2 }
+  print($a.size);   // 2
+  $a = (size=10, color="blue");
+  print($a.color);  // blue
+}
 ```
 
-## Type conversion
+### Type Conversion
 
 Integer and pointer types can be converted using explicit type conversion with an expression like:
 
 ```
-$y = (uint32) $z;
-$py = (int16 *) $pz;
+$y = (uint32)$z;
+$py = (int16 *)$pz;
 ```
 
 Integer casts to a higher rank are sign extended.
@@ -1688,8 +1914,8 @@ Conversion to a lower rank is done by zeroing leading bits.
 It is also possible to cast between integers and integer arrays using the same syntax:
 
 ```
-$a = (uint8[8]) 12345;
-$x = (uint64) $a;
+$a = (uint8[8])12345;
+$x = (uint64)$a;
 ```
 
 Both the cast and the destination type must have the same size.
@@ -1697,7 +1923,36 @@ When casting to an array, it is possible to omit the size which will be determin
 
 Integers are internally represented as 64 bit signed. If you need another representation, you may cast to the supported [Data Types](#data-types).
 
-### Array casts
+#### Cast Parsing
+
+Most C-style casting is supported, however due to bpftrace's builtins, which are raw identifiers (e.g. `pid`), and macros which can be called without parenthesis if the macro doesn't have any arguments, a raw identifier wrapped in parenthesis is considered a type when followed by something that looks like an expression start.
+
+```
+$w = (myident); // parsed as an expression and not a type
+$x = (myident)*$a; // parsed as a cast to myident type with a dereference of $a
+$y = (pid)*tid; // parsed a multiplication of the pid builtin and the tid builtin
+$z = (myident)*arg0; // parsed as a cast to myident with a dereference of builtin arg0
+```
+
+Bare identifiers in type contexts (casts, `sizeof`, `typeof`, etc.) are always treated
+as type names and are never expanded as macros. To force macro expansion in a
+type context, use the call syntax or wrap with `typeof`:
+
+```
+macro uint64_t() { 1 }
+$x = sizeof(uint64_t);           // uint64_t is treated as a type name and $x evaluates to 8
+$x = sizeof(uint64_t());         // call syntax forces macro expansion and $x evaluates to 1
+$y = (typeof(uint64_t()))$z;     // typeof wrapper for casts and this becomes $y = (typeof({ 1 }))$z;
+```
+
+Additionally, in a type context the array syntax is always treated as a type unless surrounded by parenthesis.
+```
+$x = sizeof(ident[1]);   // parsed as an array of type ident with 1 element
+$x = sizeof((ident)[1]); // parsed as an expression accessing the first element of ident (useful in macro contexts)
+$x = sizeof((ident[1])); // parsed as an expression accessing the first element of ident (useful in macro contexts)
+```
+
+#### Array Casts
 
 It is possible to cast between integer arrays and integers.
 Both the source and the destination type must have the same size.
@@ -1724,11 +1979,62 @@ Array casting allows seamless comparison of such representations:
 
 ```
 fentry:tcp_connect {
-    if (args->sk->__sk_common.skc_daddr == (uint32)pton("127.0.0.1"))
+    if (args.sk.__sk_common.skc_daddr == (uint32)pton("127.0.0.1"))
         ...
 }
 ```
 
+##### Endianness and Memory Layout
+
+When casting an integer to an array, bpftrace initializes the array from the
+source integer's underlying representation. No byte swapping is performed, so
+the result depends on the source integer type, the target array element type,
+and the system's native byte order (endianness)
+
+For example, when casting `(uint16)1` to `bool[2]`, each `bool` element is
+initialized from one byte of the `uint16` representation:
+
+```
+// On little-endian systems:
+begin { @a = (bool[2])(uint16)1; }
+// Output: @a: [true, false]
+// Explanation: uint16 value 1 = 0x0001 is represented as:
+//              [0x01, 0x00]
+//              array[0] is initialized from 0x01 (true)
+//              array[1] is initialized from 0x00 (false)
+
+// On big-endian systems:
+begin { @a = (bool[2])(uint16)1; }
+// Output: @a: [false, true]
+// Explanation: uint16 value 1 = 0x0001 is represented as:
+//              [0x00, 0x01]
+//              array[0] is initialized from 0x00 (false)
+//              array[1] is initialized from 0x01 (true)
+```
+
+The source integer type also affects the representation used for the array
+initialization. If the value is explicitly cast to a wider integer type first,
+then the array is initialized from that wider representation.
+
+For example, `(uint64)12345` is `0x0000000000003039`, so casting it to
+`int8[8]` uses the full 8-byte representation:
+
+```
+// On little-endian systems:
+begin { printf("first byte: %x\n", ((int8[8])(uint64)12345)[0]); }
+// Output: first byte: 39
+// Explanation: (uint64)12345 = 0x0000000000003039 is represented as:
+//              [0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
+//              array[0] is initialized from 0x39.
+
+// On big-endian systems:
+begin { printf("first byte: %x\n", ((int8[8])(uint64)12345)[0]); }
+// Output: first byte: 0
+// Explanation: (uint64)12345 = 0x0000000000003039 is represented as:
+//              [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x39]
+//              array[0] is initialized from 0x00.
+```
+
 ## Variables and Maps
 
 bpftrace knows two types of variables, 'scratch' and 'map'.
@@ -1783,7 +2089,6 @@ Currently these are available in bpftrace:
 - lruhash (BPF_MAP_TYPE_LRU_HASH)
 - percpuhash (BPF_MAP_TYPE_PERCPU_HASH)
 - percpulruhash (BPF_MAP_TYPE_LRU_PERCPU_HASH)
-- percpuarray (BPF_MAP_TYPE_PERCPU_ARRAY)
 
 Additionally, map declarations must supply a single argument: ***max entries*** e.g. `let @a = lruhash(100);`
 All maps that are not declared in the global scope utilize the default set in the config variable "max_map_keys".
@@ -2078,4 +2383,4 @@ config = {
 }
 ```
 
-Note: all unstable features are subject to change and/or removal.
\ No newline at end of file
+Note: all unstable features are subject to change and/or removal.
diff --git a/src/pages/docs/release_027/stdlib.md b/src/pages/docs/release_027/stdlib.md
index 55070ea..c4b3618 100644
--- a/src/pages/docs/release_027/stdlib.md
+++ b/src/pages/docs/release_027/stdlib.md
@@ -19,6 +19,11 @@ Basically all functions or macros that don't have arguments or have default argu
 Simple assertion macro that will exit the entire script with an error code if the condition is not met.
 
 
+### assert_str
+
+Checks that this value is string-like.
+
+
 ### bswap
 - `uint8 bswap(uint8 n)`
 - `uint16 bswap(uint16 n)`
@@ -152,17 +157,40 @@ interval:s:10 {
 ### comm
 - `string comm()`
 - `string comm`
+- `string comm(uint32 pid)`
 
-Name of the current thread
+Name of the current thread or the process with the specified PID
 
 This utilizes the BPF helper `get_current_comm`
 
 
+### config
+- `Record config()`
+- `Record config`
+
+Returns a `Record` containing the current `bpftrace` configuration settings. [List of config variables](./language#config-variables)
+
+```
+printf("max_strlen: %d, stack_mode: %s\n", config.max_strlen, config.stack_mode);
+}
+```
+
+
+### container_of
+- `Container* container_of(Member* ptr, Type type, Identifier member)`
+
+Returns a pointer to an object of the passed `type` given a pointer to the `member` of that object.
+
+
 ### cpid
 - `uint32 cpid()`
 - `uint32 cpid`
 
-Child process ID, if bpftrace is invoked with `-c`
+Child process ID, if bpftrace is invoked with `-c`.
+
+If there is no child process, a runtime warning will be issued and the
+return value will be zero.  This warning can be avoided by using `has_cpid`
+to check if `cpid` has a value, prior to referencing `cpid`.
 
 
 ### cpu
@@ -185,6 +213,11 @@ Pointer to `struct task_struct` of the current task
 This utilizes the BPF helper `get_current_task`
 
 
+### default_str_length
+
+Returns the default unbounded length.
+
+
 ### delete
 - `bool delete(map m, mapkey k)`
 - deprecated `bool delete(mapkey k)`
@@ -231,6 +264,35 @@ kprobe:dummy {
 ```
 
 
+### dw_ustack
+- `ustack_t dw_ustack([StackMode mode, ][int limit])`
+
+DWARF-based user-space stack unwinding. Unlike [ustack](#ustack), which
+relies on frame pointers, `dw_ustack` uses DWARF `.eh_frame` debug
+information to unwind the stack. This makes it possible to collect complete
+user-space stack traces from programs compiled without frame pointers.
+
+The signature and output format are the same as `ustack`.
+
+Bpftrace needs to read the DWARF information for the target processes at startup.
+For this, one or more pids have to be specified. This can either be done via
+`-p`, `-c` (implicitly) or `--dwarf-pid`. If `dw_ustack` cannot find unwind
+information for a process, a runtime warning is emitted.
+
+`dw_ustack` is currently only available on x86_64.
+
+**Unstable feature**
+
+`dw_ustack` is an unstable feature. By default a warning is printed when it
+is used. Set the config flag to suppress the warning or to make it an error:
+
+```
+config = { unstable_dw_ustack=enable }
+```
+
+For usage examples see [ustack](#ustack).
+
+
 ### elapsed
 - `uint64 elapsed()`
 - `uint64 elapsed`
@@ -280,22 +342,54 @@ BEGIN {
 ```
 
 
+### fail
+- `void fail(const string fmt, args...)`
+
+`fail()` formats and prints data (similar to [`printf`](#printf)) as an error message with the source location but, as opposed to [`errorf`](#errorf), is treated like a static assert and halts compilation if it is visited. All args have to be literals since they are evaluated at compile time.
+
+```
+BEGIN { if ($1 < 2) { fail("Expected the first positional param to be greater than 1. Got %d", $1); } }
+```
+
+
+
+### find
+- `boolean find(map m, mapkey k, mapvalue result)`
+
+Return `true` if the key exists in this map and sets the passed scratch variable (result) to the value of that map key.
+Otherwise return `false` and don't mutate result.
+Use this instead of `has_key` and a map access to avoid an additional map lookup.
+Error if called with a map that has no keys (aka scalar map).
+
+```
+kprobe:dummy {
+  @map[2] = (1, "hello");
+  let $val;
+  if (find(@map, 2, $val)) {
+    print($val); // prints (1, "hello")
+  }
+}
+```
+
+
 ### func
-- `string func()`
-- `string func`
+- `ksym_t func()`
+- `ksym_t func`
+- `usym_t func()`
+- `usym_t func`
 
 Name of the current function being traced (kprobes,uprobes,fentry)
 
 
 ### getopt
 - `bool getopt(string arg_name)`
-- `string getopt(string arg_name, string default_value)`
-- `int getopt(string arg_name, int default_value)`
-- `bool getopt(string arg_name, bool default_value)`
+- `bool getopt(string arg_name, bool default_value, [string description])`
+- `int getopt(string arg_name, int default_value, [string description])`
+- `string getopt(string arg_name, string default_value, [string description])`
 
 Get the named command line argument/option e.g.
 ```
-# bpftrace -e 'BEGIN { print(getopt("hello", 1)); }' -- --hello=5
+# bpftrace -e 'BEGIN { print(getopt("hello", 1, "Description of hello")); }' -- --hello=5
 
 ```
 
@@ -303,9 +397,12 @@ Get the named command line argument/option e.g.
 If no default type is provided, the option is treated like a boolean arg e.g. `getopt("hello")` would evaluate to `false` if `--hello` is not specified on the command line or `true` if `--hello` is passed or set to one of the following values: `true`, `1`.
 Additionally, boolean args accept the following false values: `0`, `false` e.g. `--hello=false`.
 If the arg is not set on the command line, the default value is used.
+`getopt` calls may optionally specify a string with the argument description (except for a boolean arg without a default value).
+
+You can use `--help` to see all named arguments/options.
 
 ```
-# bpftrace -e 'BEGIN { print((getopt("aa", 10), getopt("bb", "hello"), getopt("cc"), getopt("dd", false))); }' -- --cc --bb=bye
+# bpftrace -e 'BEGIN { print((getopt("aa", 10, "Description of aa"), getopt("bb", "hello"), getopt("cc"), getopt("dd", false))); }' -- --cc --bb=bye
 
 ```
 
@@ -319,13 +416,19 @@ Group ID of the current thread, as seen from the init namespace
 This utilizes the BPF helper `get_current_uid_gid`
 
 
+### has_cpid
+- `bool has_cpid()`
+- `bool has_cpid`
+
+Returns true iff cpid is available.
+
+
 ### has_key
 - `boolean has_key(map m, mapkey k)`
 
-Return true (1) if the key exists in this map.
-Otherwise return false (0).
+Return `true` if the key exists in this map.
+Otherwise return `false`.
 Error if called with a map that has no keys (aka scalar map).
-Return value can also be used for scratch variables and map keys/values.
 
 ```
 kprobe:dummy {
@@ -338,13 +441,66 @@ kprobe:dummy {
     if (has_key(@scalar)) { // error
       print(("hello"));
     }
+}
+```
+
+
+### is_array
+- `bool is_array(any expression)`
+
+Determine whether the given expression is an array.
+
+
+### is_err
+- `bool is_err(void * ptr)`
+
+Returns true if the pointer is an ERR_PTR, i.e. it encodes a kernel error code.
+
+In the Linux kernel, some functions return error codes encoded as pointers
+using the `ERR_PTR` macro. These are pointer values in the range
+`(unsigned long)(-4095)` to `(unsigned long)(-1)`.
 
-    $a = has_key(@associative, (1,2)); // ok
-    @b[has_key(@associative, (1,2))] = has_key(@associative, (1,2)); // ok
+This is equivalent to the kernel's `IS_ERR()` macro.
+
+```
+fexit:do_filp_open {
+  if (is_err(retval)) {
+    printf("error: %ld\n", (int64)retval);
+  }
 }
 ```
 
 
+### is_integer
+- `bool is_integer(any expression)`
+
+Determine whether the given expression is an integer.
+
+
+### is_literal
+- `bool is_literal(Expression expr)`
+
+Returns true if the passed expression is a literal, e.g. 1, true, "hello"
+
+
+### is_ptr
+- `bool is_ptr(any expression)`
+
+Determine whether the given expression is a pointer.
+
+
+### is_str
+- `bool is_str(any expression)`
+
+Determine whether the given expression is a string.
+
+
+### is_unsigned_integer
+- `bool is_unsigned_integer(any expression)`
+
+Determine whether the given expression is an unsigned integer.
+
+
 ### jiffies
 - `uint64 jiffies()`
 - `uint64 jiffies`
@@ -390,6 +546,22 @@ interval:s:1 {
 You can find all kernel symbols at `/proc/kallsyms`.
 
 
+### kfunc_allowed
+- `boolean kfunc_allowed(const string kfunc)`
+
+Determine if a kfunc is supported for particular probe types.
+
+Argument kfunc must be string literal.
+
+
+### kfunc_exist
+- `boolean kfunc_exist(const string kfunc)`
+
+Determine if a kfunc exists using BTF.
+
+Argument kfunc must be string literal.
+
+
 ### kptr
 - `T * kptr(T * ptr)`
 
@@ -401,7 +573,7 @@ The pointer type is left unchanged.
 ### kstack
 - `kstack_t kstack([StackMode mode, ][int limit])`
 
-These are implemented using BPF stack maps.
+There are several [formatting/StackMode options](./language#stack_mode).
 
 ```
 kprobe:ip_output { @[kstack()] = count(); }
@@ -440,8 +612,9 @@ kprobe:ip_output { @[kstack(3)] = count(); }
  */
 ```
 
-You can also choose a different output format.
-Available formats are `bpftrace`, `perf`, and `raw` (no symbolication):
+Note: If a limit is used and `show_debug_info` is enabled then the number of symbolized frames might exceed that limit in the output as `limit` refers to instruction pointers, which can translate to multiple inlined symbols.
+
+Example using `perf` StackMode:
 
 ```
 kprobe:ip_output { @[kstack(perf, 3)] = count(); }
@@ -480,6 +653,26 @@ kprobe:do_nanosleep
 ```
 
 
+### leader_comm
+- `string leader_comm()`
+- `string leader_comm`
+- `string leader_comm(struct task_struct * task)`
+
+Get the thread name of the thread group leader for the passed task or the current task if called without arguments.
+This is an alias for `task.group_leader.comm`, which is different than `task.real_parent.comm`, which you can get from calling `pcomm()`.
+See `pcomm()` for more details.
+
+
+### leader_tid
+- `string leader_tid()`
+- `string leader_tid`
+- `string leader_tid(struct task_struct * task)`
+
+Get the thread id of the thread group leader for the passed task or the current task if called without arguments.
+This is an alias for `task.group_leader.pid`, which is different than `task.real_parent.pid`, which you can get from calling `ppid()`.
+See `ppid()` for more details.
+
+
 ### len
 - `int64 len(map m)`
 - `int64 len(ustack stack)`
@@ -510,6 +703,16 @@ kprobe:arp_create {
 ```
 
 
+### memcmp
+- `int memcmp(left, right, uint64 count)`
+
+Compares the first 'count' bytes of two expressions.
+0 is returned if they are the same.
+negative value if the first differing byte in left is less
+than the corresponding byte in right.
+
+
+
 ### ncpus
 - `uint64 ncpus()`
 - `uint64 ncpus`
@@ -519,6 +722,7 @@ Number of CPUs
 
 ### nsecs
 - `timestamp nsecs([TimestampMode mode])`
+- `timestamp nsecs`
 - `nsecs(monotonic) - nanosecond timestamp since boot, exclusive of time the system spent suspended (CLOCK_MONOTONIC)`
 - `nsecs(boot) - nanoseconds since boot, inclusive of time the system spent suspended (CLOCK_BOOTTIME)`
 - `nsecs(tai) - TAI timestamp in nanoseconds (CLOCK_TAI)`
@@ -646,6 +850,15 @@ If `size` is smaller than the resolved path, the resulting string will be trunca
 This function can only be used by functions that are allowed to, these functions are contained in the `btf_allowlist_d_path` set in the kernel.
 
 
+### pcomm
+- `string pcomm()`
+- `string pcomm`
+- `string pcomm(struct task_struct * task)`
+
+Get the name of the parent process for the passed task or the current task if called without arguments.
+This is an alias for `task.real_parent.comm`, which is different than `task.group_leader.comm`, which you can get from calling `leader_comm()`.
+
+
 ### percpu_kaddr
 - `uint64 *percpu_kaddr(const string name)`
 - `uint64 *percpu_kaddr(const string name, int cpu)`
@@ -671,7 +884,7 @@ be rejected.
 interval:s:1 {
   $runqueues = (struct rq *)percpu_kaddr("runqueues", 0);
   if ($runqueues != 0) {         // The check is mandatory here
-    print($runqueues->nr_running);
+    print($runqueues.nr_running);
   }
 }
 ```
@@ -689,16 +902,110 @@ Defaults to `curr_ns`.
 
 
 ### ppid
+- `uint32 ppid()`
+- `uint32 ppid`
 - `uint32 ppid(struct task_struct * task)`
 
-Get the pid of the parent process
+Get the pid of the parent process for the passed task or the current task if called without arguments.
+This is an alias for `task.real_parent.pid`, which is different than `task.group_leader.pid`, which you can get from calling `leader_tid()`.
 
 
 ### print
 - `void print(T val)`
+- `void print(T val)`
+- `void print(@map)`
+- `void print(@map, uint64 top)`
+- `void print(@map, uint64 top, uint64 div)`
 
 **async**
 
+`print` prints a the value, which can be a map or a scalar value, with the default formatting for the type.
+
+```
+interval:s:1 {
+  print(123);
+  print("abc");
+  exit();
+}
+
+/*
+ * Sample output:
+ * 123
+ * abc
+ */
+```
+
+```
+interval:ms:10 { @=hist(rand); }
+interval:s:1 {
+  print(@);
+  exit();
+}
+```
+
+Prints:
+
+```
+@:
+[16M, 32M)             3 |@@@                                                 |
+[32M, 64M)             2 |@@                                                  |
+[64M, 128M)            1 |@                                                   |
+[128M, 256M)           4 |@@@@                                                |
+[256M, 512M)           3 |@@@                                                 |
+[512M, 1G)            14 |@@@@@@@@@@@@@@                                      |
+[1G, 2G)              22 |@@@@@@@@@@@@@@@@@@@@@@                              |
+[2G, 4G)              51 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
+```
+
+Declared maps and histograms are automatically printed out on program termination.
+
+Note that maps are printed by reference while scalar values are copied.
+This means that updating and printing maps in a fast loop will likely result in bogus map values as the map will be updated before userspace gets the time to dump and print it.
+
+The printing of maps supports the optional `top` and `div` arguments.
+`top` limits the printing to the top N entries with the highest integer values
+
+```
+BEGIN {
+  $i = 11;
+  for $elem : 1..$i {
+    @[$elem] = $elem-1;
+  }
+  print(@, 2);
+  clear(@);
+  exit()
+}
+
+/*
+ * Sample output:
+ * @[9]: 8
+ * @[10]: 9
+ */
+```
+
+The `div` argument scales the values prior to printing them.
+Scaling values before storing them can result in rounding errors.
+Consider the following program:
+
+```
+kprobe:f {
+  @[func] += arg0/10;
+}
+```
+
+With the following sequence as numbers for arg0: `134, 377, 111, 99`.
+The total is `721` which rounds to `72` when scaled by 10 but the program would print `70` due to the rounding of individual values.
+
+Changing the print call to `print(@, 5, 2)` will take the top 5 values and scale them by 2:
+
+```
+@[6]: 3
+@[7]: 3
+@[8]: 4
+@[9]: 4
+@[10]: 5
+```
+
 
 ### printf
 - `void printf(const string fmt, args...)`
@@ -715,10 +1022,12 @@ Values are copied and passed by value.
 bpftrace supports all the typical format specifiers like `%llx` and `%hhu`.
 The non-standard ones can be found in the table below:
 
-| Specifier | Type | Description |
-| --- | --- | --- |
-| r | buffer | Hex-formatted string to print arbitrary binary content returned by the [buf](#buf) function. |
-| rh | buffer | Prints in hex-formatted string without `\x` and with spaces between bytes (e.g. `0a fe`) |
+| Specifier | Type | Format | Description |
+| --- | --- | --- | --- |
+| r | buffer | normal hex | Hex-formatted string to print arbitrary binary content returned by the [buf](#buf) function. |
+| rh | buffer | formatted hex | Prints in hex-formatted string without `\x` and with spaces between bytes (e.g. `0a fe`) |
+| rx | buffer | escaped hex | Prints in hex-formatted string with `\x` without spaces between bytes (e.g. `\x0a\xfe`) |
+| gr | integer | human readable | Formats GFP (Get Free Pages) flags into human-readable strings, similar to Linux kernel's `%pGg` format. |
 
 `printf()` can also symbolize enums as strings. User defined enums as well as enums
 defined in the kernel are supported. For example:
@@ -741,6 +1050,22 @@ yields:
 6, SKB_DROP_REASON_SOCKET_FILTER, CUSTOM_ENUM
 ```
 
+The `%gr` specifier can be used to format GFP (Get Free Pages) flags into human-readable strings:
+
+```
+tracepoint:kmem:kmalloc {
+  printf("GFP flags: %gr\n", args->gfp_flags);
+}
+```
+
+This would output something like:
+
+```
+GFP flags: GFP_KERNEL
+GFP flags: GFP_ATOMIC|__GFP_HIGHMEM
+GFP flags: __GFP_IO|__GFP_FS|__GFP_DIRECT_RECLAIM
+```
+
 Colors are supported too, using standard terminal escape sequences:
 
 ```
@@ -757,6 +1082,16 @@ Name of the fully expanded probe
 For example: `kprobe:do_nanosleep`
 
 
+### probetype
+- `string probetype()`
+- `string probetype`
+
+Name of the probe type.
+Note: `begin` and `end` probes are of type `special`.
+
+For example: `kprobe`, `special`, `tracepoint`
+
+
 ### pton
 - `char addr[4] pton(const string *addr_v4)`
 - `char addr[16] pton(const string *addr_v6)`
@@ -767,6 +1102,11 @@ For example: `kprobe:do_nanosleep`
 `pton` infers the address family based on `.` or `:` in the given argument.
 `pton` comes in handy when we need to select packets with certain IP addresses.
 
+When casting the result of `pton()` to an integer (e.g. `(uint32)pton("127.0.0.1")`), the resulting value depends on the system's endianness. The byte array returned by `pton()` is stored in network byte order (big-endian), and when cast to an integer, it is interpreted according to the system's native byte order:
+**Little-endian systems**: The bytes are reversed when interpreted as an integer. For example, `(uint32)pton("127.0.0.1")` yields `0x100007f` (bytes: `[0x7f, 0x00, 0x00, 0x01]` interpreted as little-endian).
+**Big-endian systems**: The bytes maintain their network byte order. For example, `(uint32)pton("127.0.0.1")` yields `0x7f000001` (bytes: `[0x7f, 0x00, 0x00, 0x01]` interpreted as big-endian).
+This behavior is consistent with how the underlying `inet_pton()` function works.
+
 
 ### rand
 - `uint32 rand()`
@@ -805,13 +1145,12 @@ For kretprobe and uretprobe, its type is uint64, but for fexit it depends. You c
 
 **unsafe**
 
-**Kernel** 5.3
-
-This utilizes the BPF helper `bpf_send_signal`
+This utilizes the BPF helper `bpf_send_signal`.
 
 Probe types: k(ret)probe, u(ret)probe, USDT, profile
 
-Send a signal to the process being traced.
+Send a signal to the process being traced (any thread).
+Use `signal_thread` to send to the thread being traced.
 The signal can either be identified by name, e.g. `SIGSTOP` or by ID, e.g. `19` as found in `kill -l`.
 
 ```
@@ -826,6 +1165,34 @@ Trace/breakpoint trap (core dumped)
 ```
 
 
+### signal_name
+- `string signal_name(int sig)`
+
+Convert signal code to string.
+
+```
+#include 
+begin {
+  print(signal_name(SIGINT));
+}
+```
+
+
+### signal_thread
+- `void signal_thread(const string sig)`
+- `void signal_thread(uint32 signum)`
+
+**unsafe**
+
+This utilizes the BPF helper `bpf_send_signal_thread`.
+
+Probe types: k(ret)probe, u(ret)probe, USDT, profile
+
+Send a signal to the thread being traced.
+Use `signal` to send to the process being traced (any thread).
+The signal can either be identified by name, e.g. `SIGSTOP` or by ID, e.g. `19` as found in `kill -l`.
+
+
 ### sizeof
 - `uint64 sizeof(TYPE)`
 - `uint64 sizeof(EXPRESSION)`
@@ -860,12 +1227,12 @@ Usage
 ```
 # cat dump.bt
 fentry:napi_gro_receive {
-  $ret = skboutput("receive.pcap", args.skb, args.skb->len, 0);
+  $ret = skboutput("receive.pcap", args.skb, args.skb.len, 0);
 }
 
 fentry:dev_queue_xmit {
   // setting offset to 14, to exclude ethernet header
-  $ret = skboutput("output.pcap", args.skb, args.skb->len, 14);
+  $ret = skboutput("output.pcap", args.skb, args.skb.len, 14);
   printf("skboutput returns %d\n", $ret);
 }
 
@@ -895,8 +1262,8 @@ This function returns a `uint64` unique number on success, or 0 if **sk** is NUL
 ```
 fentry:tcp_rcv_established
 {
-  $cookie = socket_cookie(args->sk);
-  @psize[$cookie] = hist(args->skb->len);
+  $cookie = socket_cookie(args.sk);
+  @psize[$cookie] = hist(args.skb.len);
 }
 ```
 
@@ -917,6 +1284,12 @@ Prints:
 ```
 
 
+### static_assert
+- `void static_assert(bool condition, string msg)`
+
+Assert something is true or fail the build.
+
+
 ### str
 - `string str(char * data [, uint32 length)`
 
@@ -926,39 +1299,58 @@ This utilizes the BPF helpers `probe_read_str, probe_read_{kernel,user}_str`
 The maximum string length is limited by the `BPFTRACE_MAX_STRLEN` env variable, unless `length` is specified and shorter than the maximum.
 In case the string is longer than the specified length only `length - 1` bytes are copied and a NULL byte is appended at the end.
 
-When available (starting from kernel 5.5, see the `--info` flag) bpftrace will automatically use the `kernel` or `user` variant of `probe_read_{kernel,user}_str` based on the address space of `data`, see [Address-spaces](./language#address-spaces) for more information.
+bpftrace will automatically use the `kernel` or `user` variant of `probe_read_{kernel,user}_str` based on the address space of `data`, see [Address-spaces](./language#address-spaces) for more information.
+
+
+### str_concat
+- `string str_concat(string s1, string s2)`
+
+Concatenate two strings into a new string.
+Returns the new string.
+
+
+
+### strcap
+- `int64 strcap(string exp)`
+- `int64 strcap(int8 exp[])`
+- `int64 strcap(int8 *exp)`
+
+Returns the "capacity" of a string-like object.
+
+In most cases this is the same as the length, but for bpftrace-native
+strings and arrays, this is the underlying object capacity. This is used to
+bound searches and lookups without needing to scan the string itself.
 
 
 ### strcontains
-- `int64 strcontains(const char *haystack, const char *needle)`
+- `bool strcontains(string haystack, string needle)`
 
-`strcontains` compares whether the string haystack contains the string needle.
-If needle is contained `1` is returned, else zero is returned.
+Compares whether the string haystack contains the string needle.
 
-bpftrace doesn’t read past the length of the shortest string.
+If needle is contained then true is returned, else false is returned.
 
 
 ### strerror
-- `strerror_t strerror(int error)`
+- `string strerror(int error)`
 
 Convert errno code to string.
-This is done asynchronously in userspace when the strerror value is printed, hence the returned value can only be used for printing.
 
 ```
 #include 
-BEGIN {
+begin {
   print(strerror(EPERM));
 }
 ```
 
 
 ### strftime
-- `timestamp strftime(const string fmt, int64 timestamp_ns)`
+- `timestamp strftime(const string fmt, uint64 timestamp_ns)`
 
 **async**
 
 Format the nanoseconds since boot timestamp `timestamp_ns` according to the format specified by `fmt`.
 The time conversion and formatting happens in user space, therefore  the `timestamp` value returned can only be used for printing using the `%s` format specifier.
+**Note:** `timestamp_ns` must be non-negative. Negative timestamp literals are rejected.
 
 bpftrace uses the `strftime(3)` function for formatting time and supports the same format specifiers.
 
@@ -975,6 +1367,14 @@ bpftrace also supports the following format string extensions:
 | `%f` | Microsecond as a decimal number, zero-padded on the left |
 
 
+### strlen
+- `uint64 strlen(string exp)`
+- `uint64 strlen(int8 exp[])`
+- `uint64 strlen(int8 *exp)`
+
+Returns the length of a string-like object.
+
+
 ### strncmp
 - `int64 strncmp(char * s1, char * s2, int64 n)`
 
@@ -986,6 +1386,25 @@ bpftrace doesn’t read past the length of the shortest string.
 The use of the `==` and `!=` operators is recommended over calling `strncmp` directly.
 
 
+### strstr
+- `int64 strstr(string haystack, string needle)`
+
+Returns the index of the first occurrence of the string needle in the string haystack. If needle is not in haystack then -1 is returned.
+
+
+### syscall_name
+- `string syscall_name(int nr_syscall)`
+
+Convert syscall number to string.
+
+```
+#include 
+begin {
+  print(syscall_name(__NR_read)); // outputs "read"
+}
+```
+
+
 ### system
 - `void system(string namefmt [, ...args])`
 
@@ -1065,6 +1484,19 @@ Unlike `strftime()` `time()` doesn’t send a timestamp from the probe, instead
 bpftrace uses the `strftime(3)` function for formatting time and supports the same format specifiers.
 
 
+### typeof
+- `TYPE typeof(TYPE)`
+- `TYPE typeof(EXPRESSION)`
+
+This is a special builtin that can only be used in the following contexts:
+- variable declarations e.g. `let $a: typeof($b);`
+- cast expressions e.g. `$a = (typeof($b))2;`
+- macro expansion calls to pass a type parameter e.g. `my_macro(typeof(uint64 *));`
+- type accepting builtins: `sizeof`, `offsetof`, `typeinfo`
+
+This builtin evaluates to the static type of either the parsed TYPE or the raw EXPRESSION parameter.
+
+
 ### uaddr
 - `T * uaddr(const string sym)`
 
@@ -1074,7 +1506,7 @@ bpftrace uses the `strftime(3)` function for formatting time and supports the sa
 * uretprobes
 * USDT
 
-***Does not work with ASLR, see issue [#75](https://github.com/bpftrace/bpftrace/issues/75)***
+If kernel supports task_vma open-coded iterator kfuncs (linux >= 6.7), uaddr() will correct the symbol addresses of PIE and dynamic libraries instead of directly using the symbol addresses in the ELF file, see https://github.com/torvalds/linux/commit/4ac454682158.
 
 The `uaddr` function returns the address of the specified symbol.
 This lookup happens during program compilation and cannot be used dynamically.
@@ -1136,7 +1568,7 @@ Often this is just "root"
 ### ustack
 - `ustack_t ustack([StackMode mode, ][int limit])`
 
-These are implemented using BPF stack maps.
+There are several [formatting/StackMode options](./language#stack_mode).
 
 ```
 kprobe:do_sys_open /comm == "bash"/ { @[ustack()] = count(); }
@@ -1190,8 +1622,9 @@ kprobe:ip_output { @[ustack(3)] = count(); }
  */
 ```
 
-You can also choose a different output format.
-Available formats are `bpftrace`, `perf`, and `raw` (no symbolication):
+Note: If a limit is used and `show_debug_info` is enabled then the number of symbolized frames might exceed that limit in the output as `limit` refers to instruction pointers, which can translate to multiple inlined symbols.
+
+Example using `perf` StackMode:
 
 ```
 kprobe:ip_output { @[ustack(perf, 3)] = count(); }
@@ -1236,6 +1669,49 @@ uprobe:/bin/bash:readline
 ```
 
 
+### warnf
+- `void warnf(const string fmt, args...)`
+
+**async**
+
+`warnf()` formats and prints data (similar to [`printf`](#printf)) as an warning message with the source location. This respects the "--no-warnings" flag and will be silent if that is used.
+
+```
+BEGIN { warnf("Something kinda bad with args: %d, %s", 10, "arg2"); }
+```
+
+Prints:
+
+```
+EXPECT stdin:1:9-62: WARNING: Something kinda bad with args: 10, arg2
+```
+
+
+### write_user
+- `bool write_user(T * dst, T * src, uint32 len)`
+
+**unsafe**
+
+Writes `len` bytes from BPF program memory at `src` to user-space address
+`dst` using the BPF helper `bpf_probe_write_user`.
+
+Returns true on success, or false on failure.
+
+**Warning**: This can crash or corrupt the target process if used incorrectly.
+Only use on user-space memory addresses belonging to the current task.
+The kernel will print a warning to dmesg when this helper is used.
+
+```
+tracepoint:syscalls:sys_enter_openat
+/comm == "myapp"/ {
+  $new_path = "/tmp/redirected\0";
+  if (write_user(args.filename, $new_path, 16)) {
+    printf("redirected open for pid %d\n", pid);
+  }
+}
+```
+
+
 ### zero
 - `void zero(map m)`
 
@@ -1654,4 +2130,4 @@ Maps are printed by reference not by value and as the value gets updated right a
 @: 10
 ```
 
-Therefore, when you need precise event statistics, it is recommended to use synchronous functions (e.g. count() and hist()) to ensure more reliable and accurate results.
\ No newline at end of file
+Therefore, when you need precise event statistics, it is recommended to use synchronous functions (e.g. count() and hist()) to ensure more reliable and accurate results.