Skip to content

Commit 3582ffc

Browse files
committed
doc: Second part of design document
Signed-off-by: Diogo Behrens <[email protected]>
1 parent 0bdd003 commit 3582ffc

File tree

1 file changed

+153
-3
lines changed

1 file changed

+153
-3
lines changed

doc/design.md

Lines changed: 153 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,9 @@ The Pubsub system introduces several key advantages:
150150
metadata to the subscribers. Actual type defined by `chain`.
151151

152152
2. **Publishing**: Publishers (such as intercept modules or the Self module)
153-
publish events by calling `PS_PUBLISH(chain_id, type_id, void*, metadata_t*)`. The
154-
publisher specifies the chain, event type, event payload, and a potentially
155-
`NULL` metadata object.
153+
publish events by calling `PS_PUBLISH(chain_id, type_id, void*,
154+
metadata_t*)`. The publisher specifies the chain, event type, event payload,
155+
and a potentially `NULL` metadata object.
156156

157157
3. **Callback Handling**: The callback function is where the action happens for
158158
the subscriber. Upon receiving an event, the callback can inspect and process
@@ -403,3 +403,153 @@ on a per-thread basis. Each thread can store its own file descriptor in its TLS
403403
space, ensuring that threads do not interfere with each other’s file operations.
404404
A subscriber could intercept system calls like open or close, logging the event
405405
and associating file descriptors with the correct thread’s TLS.
406+
407+
408+
# 5. Interpose Modules
409+
410+
In addition to the core Dice components, several interpose modules are provided
411+
with Dice. These modules are designed to intercept and modify the behavior of
412+
various system functions, allowing for detailed monitoring, testing, and
413+
debugging. Interposition is a powerful technique that allows developers to hook
414+
into existing system calls and modify their behavior or capture specific events.
415+
416+
417+
## 5.1. What is Interposition?
418+
419+
Interposition refers to the technique of intercepting calls to system functions
420+
or library functions and inserting custom behavior. In Dice, this technique is
421+
applied to various system functions using shared libraries and dynamic linking
422+
mechanisms like `LD_PRELOAD`.
423+
424+
By using interposition, Dice can intercept functions like `pthread_create`,
425+
`malloc`, `free`, and other system-level functions without modifying the source
426+
code of the application.
427+
428+
429+
## 5.2. How Interposition Works with `LD_PRELOAD`
430+
431+
The `LD_PRELOAD` environment variable is a mechanism in Unix-like systems that
432+
allows users to load shared libraries before others. When an application is run,
433+
the dynamic linker checks the `LD_PRELOAD` variable and loads any libraries
434+
listed in it before the default system libraries. This allows Dice to interpose
435+
on functions without modifying the application’s code or the system libraries
436+
themselves.
437+
438+
Each interpose module in Dice targets a specific set of functions. For example,
439+
`mod-pthread_create` module intercepts calls to `pthread_create` and
440+
`pthread_exit`, while the `mod-malloc` intercepts memory allocation functions
441+
like `malloc`, `free`, `calloc`, and `realloc`.
442+
443+
When an application calls an intercepted function, the corresponding interpose
444+
module is triggered. For example, when `pthread_create` is called, the interpose
445+
module publishes events using the Pubsub system. For example, when
446+
`pthread_create` is intercepted, the event `EVENT_THREAD_CREATE` is published.
447+
Via a trampoline function, passed to the readl `pthread_create`, the new thread
448+
also publishes a `EVENT_THREAD_INIT` event. Similarly, events like
449+
`EVENT_MUTEX_LOCK`, `EVENT_MUTEX_UNLOCK`, `EVENT_MALLOC`, and `EVENT_FREE` can
450+
be intercepted and published to the appropriate chains.
451+
452+
Notes that on macOS the environment variable to control library preloading is
453+
called `DYLD_INSERT_LIBRARIES`.
454+
455+
## 5.3. Interpose Modules in Dice
456+
457+
- `mod-pthread_create`: Intercepts the `pthread_create` and `pthread_join`
458+
functions to manage thread initialization and finalization.
459+
460+
- `mod-pthread_mutex`: Intercepts mutex functions like `pthread_mutex_lock`,
461+
`pthread_mutex_unlock`, `pthread_mutex_trylock`, etc to monitor thread
462+
synchronization events via mutexes.
463+
464+
- `mod-pthread_cond`: Intercepts condition variable functions like
465+
`pthread_cond_wait`, `pthread_cond_signal`, etc to track thread
466+
synchronization on condition variables.
467+
468+
- `mod-malloc`: Intercepts memory allocation functions like `malloc`, `free`,
469+
`calloc`, and others to track memory usage and detect leaks or abnormal memory
470+
access.
471+
472+
- `mod-cxa`: Intercepts functions related to exception handling, such as
473+
`__cxa_guard_acquire`, `__cxa_guard_release`, and other related functions.
474+
475+
- `mod-sem`: Intercepts semaphore functions like `sem_wait`, `sem_post`, etc to
476+
monitor semaphore operations.
477+
478+
- `mod-tsan`: Intercepts all functions exposed by `libtsan.so` to enable
479+
fine-grained thread safety analysis.
480+
481+
By using these interpose modules, Dice can gather detailed execution data,
482+
track thread behavior, monitor memory usage, and enable advanced testing and
483+
debugging features.
484+
485+
486+
# 6. Loading Modules
487+
488+
Dice is designed to be loaded as a shared library with `LD_PRELOAD`.
489+
The core library in Dice has only the Pubsub and the Mempool modules inside.
490+
Addtional modules can be loaded in two ways.
491+
492+
493+
## 6.1. Shared Library Modules
494+
495+
Consider a multithreaded application `foo`. To run `foo` with Dice the user
496+
simply starts it with the following command:
497+
498+
```
499+
env LD_PRELOAD=/path/to/libdice.so foo <arg1>
500+
```
501+
502+
Additional modules can be loaded with the `LD_PRELOAD` variable as well:
503+
504+
```
505+
env LD_PRELOAD=/path/to/libdice.so:/path/to/mod-pthread_create.so:... foo <arg1>
506+
```
507+
508+
Subscription callbacks are internally kept as lists of function pointers. The
509+
corresponding indirection cost has to be considered when deploying Dice in this
510+
way.
511+
512+
### Subscription Order
513+
514+
One has to be careful in which order the constructors of the modules are
515+
executed. On NetBSD the constructors are executed left-to-right. Given a list
516+
of libraries `LD_PRELOAD=libdice.so:mod1.so:mod2.so`, the subscribers in
517+
`mod1.so` will be registered earlier than subscribers in `mod2.so`; therefore,
518+
callbacks in `mod1.so` will be called first. On Linux, the constructors are
519+
executed right-to-left. So the opposite subscription order will result.
520+
521+
In general, the user has to figure out the order the constructors are called by
522+
the linker and order the shared libraries accordingly.
523+
524+
## 6.2. Monolithic Shared Library
525+
526+
Modules in Dice can also linked together with the core components in a single
527+
shared library. In this configuration, each module is a compilation unit, i.e.,
528+
a `.o` file. When compiling each of these files, the user should specify a
529+
priority for the subscription order by passing `-DDICE_XTOR_PRIO=VAL` to the
530+
compiler. `VAL` should be a number between 200 and 1000. The lower the value,
531+
the higher the priority when subscribing chains.
532+
533+
Assume the user creates a library `libmydice.so` containing `pubsub.o`,
534+
`mempool.o`, `mod-pthread_create.o` and a user-defined module `mymod.o`. To load
535+
this bundle, the user simply uses `LD_PRELOAD`:
536+
537+
```
538+
env LD_PRELOAD=/path/to/libmydice.so foo <arg1>
539+
```
540+
541+
If the user compiles all modules with LTO option, the resulting library can be
542+
much faster than relying on the approach of section 6.1. Nevertheless, without
543+
further steps, the resulting library above will use function pointers when
544+
executing the callbacks of the subscribers.
545+
546+
### Fast-Chain Module
547+
548+
Dice provides a auto-generated module called Fast-Chain module or `fastch.o`,
549+
which directly call the subscriber callbacks without relying on function
550+
pointers. To do that, the Fast-Chain module employs large switch cases on the
551+
`chain_id`, `type_id` and priority values. However, the ranges of these three
552+
dimensions has to be limited to keep the size of the generated code manageable.
553+
554+
For instructions on how to use Fast-Chain, see the `CMakeLists.txt` inside
555+
`src/fastch`.

0 commit comments

Comments
 (0)