From e14868e43bff4fa50258eadb86cbc0055020b1a9 Mon Sep 17 00:00:00 2001 From: Olivier Dion Date: Wed, 22 Dec 2021 15:15:28 -0500 Subject: [PATCH] doc/agent: Add agent documentation Change-Id: I028985ac90d07199cfa30d33e21f55ebef228ff3 --- doc/agent/build.rst | 41 +++++++++++++++ doc/agent/workflow.rst | 110 +++++++++++++++++++++++++++++++++++++++++ test/agent/README.md | 87 +------------------------------- 3 files changed, 152 insertions(+), 86 deletions(-) create mode 100644 doc/agent/build.rst create mode 100644 doc/agent/workflow.rst diff --git a/doc/agent/build.rst b/doc/agent/build.rst new file mode 100644 index 000000000..ea1134ae5 --- /dev/null +++ b/doc/agent/build.rst @@ -0,0 +1,41 @@ +================================== +Agent build steps (GNU/Linux only) +================================== + +Requirements +============ + +Guile library version 3.0.7 or higher is required. Guile lib may require other +dependencies in particular libunistring-dev and libgc-deb packages. + +Guile can be provided by the distro if available, or built locally. Note that +Guile v3.0.7 is quite recent and most likely not yet provided by most linux +distributions. If the required version is available on your distro, just +install it using your distro's package manager. Development packages must be +installed as well. + +Build Guile library +=================== + +To build Guile locally, you first need to enable it when building contrib, then +recompile contrib:: + + cd daemon/contrib/native + ../bootstrap --enable-guile + make list + make fetch + make -j + + +Compile +======= + +To compile the agent, one has to enable it while configuring the daemon. At this +stage, Guile must be already available:: + + cd daemon + ./configure --enable-agent # other options can be added if needed such as --enable-debug + cd test/agent + make check + + diff --git a/doc/agent/workflow.rst b/doc/agent/workflow.rst new file mode 100644 index 000000000..552c04c1e --- /dev/null +++ b/doc/agent/workflow.rst @@ -0,0 +1,110 @@ +============== +Agent workflow +============== + +Running the agent +================= + +The agent is actually a wrapper around Guile's Scheme shell. By default, you +enter an interactive prompt that allows you to interact with Jami using the +primitive bindings or by using the agent helper. For help for running the +agent, run ``./agent --help``. + +Guile bindings +============== + +Guile needs primitive bindings to communicate with Jami. Usually, these +bindings can written in pure Scheme using the foreign function interface (FFI) +and some dlopen() magics. However, this method cannot applies here for two main +reasons: + + 1. Jami source code is in C++ not C. + 2. Dynamic loading is not present on some platform supported by Jami. + +The first reason makes it hard to interface C++ container types and other +standard types to bytevector used by Guile to interface with foreign functions. +In C, it's trivial to just types and pointers to bytevector. + +The second reason is a constraint on the agent. Since the goal is to have a set +of bindings that can run on any platform where Jami is supported, bindings +should be registered in C++. + +All bindings can be found under ``test/agent/src/bindings/*``. Bindings should +be decouple into module that reflect their common functionality. For example, +``test/agent/src/bindings/account.h`` has all the Jami's bindings for managing +accounts. When a set of bindings is to be added to a new module, the latter has +to be registered into Guile. In order accomplish this, one has to include the +bindings and define the module under ``test/agent/src/bindings/bindings.cpp``. + +When a binding is called from Guile, the arguments passed are Scheme objects of +type ``SCM``. This is an opaque type that is generic. In order to be clear on +what the underlying type needed by the primitive procedure is, one should add +the suffix of the type at the end. + +For example, ``my_primitive_procedure()`` expects that ``some_variable_str`` +will be of type ``string``. + +There's also a set of utilities that can be used to convert C++ object to Scheme +and vice versa. These utilities can be found under ``test/agent/src/utils.h`` +and are all template based and can usually be used without specifying any type +thanks to type inference. + +For example, to convert ``std::string bar`` to ``SCM baz``, one would do ``baz = +to_guile(bar)``. One can also do the opposite like so ``bar = from_guile(baz)``. + +Examples +======== + +Examples on how to use the agent can be found under ``test/agent/examples``. + +Compiling modules +================= + +Modules such as ``(agent)`` and ``(jami logger)`` can be compiled using the +agent itself like so:: + + ./agent.exe compile /path/to/module.scm /path/to/module.scm.go + +You however do not need to do this manually. You should instead add your Scheme +(.scm) file to the list MODULES in ``tests/agent/Makefile.am`` and invoke +``make compile``. + +Demuxing outputs +================ + +While developing scenarios, you don't want Jami's output to clobber Guile's +output. One easy way to do so is by using the ``JAMI_LOG_FILE`` environment +variable. + +For example:: + + JAMI_LOG_FILE=jami.log ./agent.exe -s my-scenario.scm + +But sometime you want Jami's output on the console. The agent disable the +console output for Jami, but you can do the following as a workaround:: + + JAMI_LOG_FILE=/dev/stdout ./agent.exe -s my-scenario.scm + + +Debugging the agent +=================== + +Since ``agent.exe`` is a regular ELF executable, you can attach a debugger to +it. If you're using GDB, you will need to handle some signals, otherwise your +program will keep getting interrupted. Here's a recommended GDB file to use:: + + handle SIGPWR noprint pass + handle SIGXCPU noprint pass + + set environment XDG_CONFIG_HOME=/tmp/jami-agent + set environment XDG_CACHE_HOME=/tmp/jami-agent + set environment XDG_DATA_HOME=/tmp/jami-agent + + set environment SIPLOGLEVEL 5 + + set environment UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 + set environment ASAN_OPTIONS=print_stacktrace=1:halt_on_error=1 + + set environment JAMI_LOG_FILE jami.log + + run diff --git a/test/agent/README.md b/test/agent/README.md index 37f6cf2a7..87ee4a576 100644 --- a/test/agent/README.md +++ b/test/agent/README.md @@ -1,86 +1 @@ -# Agent build steps (GNU/Linux only) -Last revision: 2021-08-31 - -# Requirements -Guile library version 3.0.7 or higher is required. Guile lib may require other -dependencies in particular libunistring-dev and libgc-deb packages. - -Guile can be provided by the distro if available, or built locally. Note that -Guile v3.0.7 is quite recent and most likely not yet provided by most linux -distributions. -If the required version is available on your distro, just install it using your -distro's package manager. Development packages must be installed as well. - -# Build Guile library -To build Guile locally, you first need to enable it when building contrib, then -recompile contrib: - -```sh -cd daemon/contrib/native -../bootstrap --enable-guile -make list -make fetch -make -j -``` - -# Compile -To compile the agent, one has to enable it while configuring the daemon. At this -stage, Guile must be already available. - -```sh -cd daemon -./configure --enable-agent # other options can be added if needed such as --enable-debug -cd test/agent -make check -``` - -# Running the agent -The agent is actually a wrapper around Guile's Scheme shell. By default, you -enter an interactive prompt that allows you to interact with Jami using the -primitive bindings or by using the agent helper. For help for running the -agent, run `./agent --help`. - -# Guile bindings -Guile needs primitive bindings to communicate with Jami. Usually, these -bindings can written in pure Scheme using the foreign function interface (FFI) -and some dlopen() magics. However, this method cannot applies here for two main -reasons: - - 1. Jami source code is in C++ not C. - 2. Dynamic loading is not present on some platform supported by Jami. - -The first reason makes it hard to interface C++ container types and other -standard types to bytevector used by Guile to interface with foreign functions. -In C, it's trivial to just types and pointers to bytevector. - -The second reason is a constraint on the agent. Since the goal is to have a set -of bindings that can run on any platform where Jami is supported, bindings -should be registered in C++. - -All bindings can be found under `src/bindings/*`. Bindings should be decouple -into module that reflect their common functionnality. For example, -`src/bindings/account.h` has all the Jami's bindings for managing accounts. When -a set of bindings is to be added to a new module, the latter has to be -registered into Guile. In order accomplish this, one has to include the -bindings and define the module under `src/bindings/bindings.cpp`. - -When a binding is called from Guile, the arguments passed are Scheme objects of -type `SCM`. This is an opaque type that is generic. In order to be clear on -what the underlying type needed by the primitive procedure is, one should add the -suffix of the type at the end. - -For example, `my_primitive_procedure()` expects that `some_variable_str` -will be of type `string`. - -There's also a set of utilities that can be used to convert C++ object to Scheme -and vice versa. These utilities can be found under `src/utils.h` and are all -template based and can usually be used without specifying any type thanks to -type inference. - -For example, to convert `std::string bar` to `SCM baz`, onw would do -`baz = to_guile(bar)`. One can also do the oposite like so -`bar = from_guile(baz)`. - -# Examples -See `examples/` - +See doc/agent/*.