diff options
author | Ray Strode <rstrode@redhat.com> | 2012-05-07 23:54:28 -0400 |
---|---|---|
committer | Ray Strode <rstrode@redhat.com> | 2012-05-07 23:55:12 -0400 |
commit | bb8e3afe197f6b90c1a9510bbda768acabf3f0f4 (patch) | |
tree | c1d7bbdb7fb422bf3ceb510625e4538b8f3751c6 /docs | |
parent | 19f237b7cc587941eacef8328c1b5e4a508ab9e7 (diff) |
docs: do a pass over new development.txt
I just did a quick read through and made various changes here and
there.
Diffstat (limited to 'docs')
-rw-r--r-- | docs/development.txt | 246 |
1 files changed, 158 insertions, 88 deletions
diff --git a/docs/development.txt b/docs/development.txt index e1b8da36..9d6898a5 100644 --- a/docs/development.txt +++ b/docs/development.txt @@ -12,7 +12,7 @@ Developer Information for Plymouth This article gives useful information for developers of plymouth. It tries to explain the overall architecture, the most important data -structures and a howto for typical use cases, like debugging. It is +structures, and basic walk throughs for debugging. It is not meant to be a API documentation. In the future the authors try to use gtkdoc for a detailed documentation of the various functions inside the code. @@ -56,17 +56,16 @@ plymouthd is run as early as possible in the boot process. It gets normally started from the initial ramdisk loaded by the boot loader (e.g. GRUB). -Controlling plymouth is done by adding options the kernel command line -which is edited through the boot loader. These boot arguments are -typically entered at the GRUB prompt or written into grub.cfg. +The behavior of plymouthd can be somewhat controlled thorugh the +kernel command line, normally passed to the kernel from grub. Splash screen selection ~~~~~~~~~~~~~~~~~~~~~~~ -Use the following options for control the selection of a splash screen. +Use the following options to control the selection of a splash screen. - * +plymouth.splash=<name-of-slpash-to-use>+ Select the splash screen to use. + * +plymouth.splash=<name-of-splash-to-use>+ Select the splash screen to use. * +plymouth.force-splash+ Force a splash screen, even if plymouth would normally switch it off. @@ -105,33 +104,42 @@ influence plymouth. Debugging --------- -There are two different scenarios where you want to debug: a live -system executing plyouth during boot time or executing plymouth -inside a running X11 window session without booting the system. +There are three different environments that can be used to troubleshoot the plymouth daemon: + + * executing plymouth inside a running X11 window session without booting the system. + + * executing plymouth on a VT in a running system + + * a live system executing plymouth during boot time Debugging inside X11 ~~~~~~~~~~~~~~~~~~~~ -This is the easist way to debug, as you have a complete running system +This is the easiest way to debug, as you have a complete running system and you are not running in a restricted boot environment. -This works automatically if plymouth finds the renderer -"x11". Depending on your system this requires installing an additional -package, sometimes called "plymouth-devel". +This works automatically if plymouth detects and X server running, and +finds the "x11" renderer plugin". Depending on your system this requires +installing an additional package, sometimes called "plymouth-devel". Start by executing plymouthd as root with the appropriate options. - root# /sbin/plymouthd --no-daemon --debug + # /sbin/plymouthd --no-daemon --debug --tty=/dev/tty Then use plymouth to start the splash screen and control plymouthd. - root# /bin/plymouth --show-splash + # /bin/plymouth show-splash + # /bin/plymouth quit -If this works as expected then start plymouthd under debugger -control. There are various frontends for GDB, the GNU debugger. The -simplest is using its built-in text mode: +If this works as expected then you can attach to it with a debugger. +There are various frontends for GDB, the GNU debugger, though, it's +often simplest to use gdb's built-in text mode interface: - root# gdb /sbin/plymouthd + # gdb attach $(/sbin/pidof plymouthd) + +You can also start plymouthd in the debugger directly: + + # gdb /sbin/plymouthd GNU gdb (GDB) Fedora (7.3.50.20110722-13.fc16) Copyright (C) 2011 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> @@ -147,17 +155,51 @@ simplest is using its built-in text mode: See the GDB manual for more information. +Debugging plymouth without X11 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Sometimes it's necessary to debug parts of the plymouthd daemon that don't +get exercised when using the "x11" renderer plugin. (For instance, drm handling +code). + +For these cases it's possible to debug plymouth by first sshing into the machine +from another machine 3 times, and then stopping X: + +# init 3 + +Then, stopping any gettys using tty 1. This step may require running initctl, +systemctl, or editing an init config file. + +Then, running plymouthd from one of ssh sessions: + + # /sbin/plymouthd --no-daemon --debug + +Then, attaching with gdb from another ssh session: + + # gdb attach $(/sbin/pidof plymouthd) + +Then controlling plymouth from the third ssh session: + + # /bin/plymouth show-splash + # /bin/plymouth quit + + Debugging the booting live system ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This is a much more complicated setup as debugging under X11. Try hard -to reproduce your problems under X11... +It's not easy to debug plymouthd while the system is booting. When possible, +it's better to try to reproduce problems in one of the more controlled +environments mentioned above. -Use logging to watch the running plymouth. Add additional log messages -to the source code to see what is going on. Remember, that plymouthd -is part of the initial ramdisk. After compiling you have to store the -updated binary in the initial ramdisk. This could be done with -+/usr/libexec/plymouth/plymouth-update-initrd+. +The best tactic is to boot with +plymouth.debug+ on the kernel command line. +This will make plymouth spew messages that can be seen by hitting the escape +key. They can also be seen in +/var/log/plymouth-debug.log+ after boot up. +Frequently, when debugging new problems, the existing logging will be +insufficient to figure out the issue. In those cases, it may be necessary to +instrument the plymouthd code with more ply_trace() calls. + +Anytime plymouthd is changed, to test those changes on a live system, it's +important to rebuild the initial ramdisk. This can be done by running the ++/usr/libexec/plymouth/plymouth-update-initrd+ command. ********************************************************************** Please improve this part and describe how to use GDB to debug the @@ -172,14 +214,15 @@ This chapter presents the source code and its structure. Modules and source code organization ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -plymouthd consists of a binary executable and a number plugins which -are loaded on demand as a shared library using dlopen(3). +plymouthd consists of a binary executable, three shared libraries linked +at build time, and a number plugins which are loaded on demand as DSOs using +dlopen(3). ./src ├── client # plymouth - ├── libply # common utilities functions - ├── libply-splash-core # ? - ├── libply-splash-graphics # ? + ├── libply # runtime library (utility functions) + ├── libply-splash-core # splash plugin APIS + ├── libply-splash-graphics # graphical splash plugin specific APIs ├── plugins # plugins as shared libraries │ ├── controls # grapical widgets │ │ └── label # text label for text output @@ -187,7 +230,7 @@ are loaded on demand as a shared library using dlopen(3). │ │ ├── drm │ │ ├── frame-buffer │ │ └── x11 - │ └── splash # the different splash screens + │ └── splash # the different splash plugins │ ├── details │ ├── fade-throbber │ ├── script @@ -195,15 +238,17 @@ are loaded on demand as a shared library using dlopen(3). │ ├── text │ ├── throbgress │ └── two-step - ├── upstart-bridge - └── viewer + ├── upstart-bridge # code for interfacing with the upstart init system + └── viewer # small gtk boot.log viewer application + + ./themes # example themes that use the various splash plugins Communication between plymouth and plymouthd ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When plymouthd starts, it opens a UNIX domain socket, called -"/org/freedesktop/plymouthd" where it listens for commands. +When plymouthd starts, it opens an abstract UNIX domain socket, +called "/org/freedesktop/plymouthd" where it listens for commands. plymouth and plymouthd use a simple binary protocol to exchange commands and arguments defined in src/ply-boot-protocol.h. @@ -211,82 +256,107 @@ commands and arguments defined in src/ply-boot-protocol.h. Triggers ~~~~~~~~ -Plymouth is written in C with an object oriented background, similar -to gtk. One of the central concepts is the _trigger_. A trigger is an implementation of the concept of 'http://en.wikipedia.org/wiki/Closure_%28computer_science%29[closures]' for plymouth. +Plymouth is written in C, using object oriented programming practices, +a centralized, file descriptor based event loop, and callbacks, similar +to many graphical programs (that, for instance, use toolkits such +as GTK+). + +In plymouth, callback handling is facilitated using the _trigger_ APIs. +Triggers are objects that provide +'http://en.wikipedia.org/wiki/Closure_%28computer_science%29[closures]' +for plymouth. A trigger consists of a list of _handlers_. A handler is basically a pointer to a function taking some arguments. These functions are a -kind of callback. The lifetime of a trigger starts with the creation -of the trigger, appending one or more handlers and finally a pull -of the trigger. Pulling a trigger will iterate through the list of -handlers and call back each registered handler. - -The handler gets 3 arguments: +user_data+, +data+ and +trigger+. Both -data arguments allow to tranfer _context_ or _state_ or _information_ -to the handler. They are generic void pointer which allow to transport -information to the handler. The +user_data+ transfers data from the -code appending the handler to the trigger. The +data+ transfers -information from the code pulling the trigger. And the trigger itself -is also given as an argument to the handler to allow different actions -depening on the trigger. - -When is this used? Maybe you know the standard Fedora plymouth theme -"charge" which features a shadowy hull of a Fedora logo _charge_ up -and finally burst into full form. Imagine that you want to turn the -screen red when the plugin finishes. For that case, the author of -_charge_ may offer a trigger called "on_finish". You can then add your -handler called "set_background" to that trigger. Maybe you would even -give the color "red" as a user_data when adding the handler. If the -theme finishes, it will call back your handler and even transport -"red" as a parameter to the handler. This allows very flexible program -designs. - -However, there is also a drawback. The code flow of the application is -not very clear. A standard program without triggers runs sequentially -and you always know what is done next. Using triggers gives you much -improved flexibility on the cost of a hard to follow program -flow. There is always a tradeoff. :-) +kind of callback. The lifecycle of a trigger starts with its creation, +then follows with the trigger's creator setting one or handlers on the +trigger. Then the trigger gets passed off to otherwise independent +parts of the code. That code later "pulls" the trigger which causes +the trigger's handlers to be called. + +The handler is called with 3 arguments: +user_data+, +trigger_data+ and ++trigger+. Both data arguments are for transfering _context_ or _state_ +to the handler. They are generic void pointers. The +user_data+ argument +is passed to the trigger at the time a handler is added to the +trigger. It's a way for creator of the trigger to recover its state +when the handler is called. The +trigger_data+ argument transfers +information from the code pulling the trigger. It can be thought of as +a payload or result to be sent from the code pulling the trigger to +the code watching the trigger. The third argument, +trigger+, is the +the trigger itself. It's passed as a convenience, so the same handler +can be used for multiple triggers, and the handler can differentiate +which trigger fired. + +When are triggers used? They're used any time two independently +contained parts of the code need to talk to each other. + +As an example, when a user needs to input a password, the plymouth +client asks the plymouth daemon to ask the user for a password. The +daemon can't respond to the client until the user has typed the password. +The code that handles communication with the client sets up a trigger +that the keyboard reading code pulls when the user hits enter. When +that trigger fires, a function set up by the code that communcates +with the client is called and it can reply to the client with the +password delivered from the keyboard reading code through the trigger. + +This allows for a flexible, encapsulated program architecture. +However, triggers have drawbacks. The code flow of the application can +be hard to follow. There's no longer a direct, linear progression of +function calls. Programs without callbacks/triggers run sequentially. +It's always easy to see code flow. With triggers, it's not always +obvious at point in the code where a trigger is pulled, what other +parts of the code are going to get called into. So here is a simple example. [literal] #include "ply-trigger.h" - // The function creates a trigger and adds a handler to be called back. - ply_trigger_t *trigger_creator() + /* The function creates a trigger and adds a handler to be called back. */ + ply_trigger_t * + trigger_creator (void) { - // [1] Create the trigger - ply_trigger_t *onexit_trigger = ply_trigger_new (NULL); + /* [1] Create the trigger */ + ply_trigger_t *on_exit_trigger = ply_trigger_new (NULL); - // [2] Prepare the user_data to give to the handler when pulled. + /* [2] Prepare the user_data to give to the handler when pulled. */ char *user_data = "These are greetings from trigger_creator."; - // [3] Add handler and user data to trigger. + /* [3] Add handler and user data to trigger. */ ply_trigger_add_handler (onexit_trigger, - (ply_trigger_handler_t)onexit_handler, - user_data); + (ply_trigger_handler_t) + onexit_handler, + user_data); return on_exit_trigger; } - // This function pulls the trigger. - void trigger_puller(ply_trigger_t *trigger) + /* This function pulls the trigger. */ + void + trigger_puller (ply_trigger_t *trigger) { char *data = "trigger_puller pulled you."; - ply_trigger_pull(trigger, data); + ply_trigger_pull (trigger, data); } - // This is the handler which gets called back when the trigger is pulled. - void onexit_handler(char *user_data, char *data, ply_trigger_t *trigger) + /* This is the handler which gets called back when the trigger is pulled. */ + void + onexit_handler (char *user_data, + char *trigger_data, + ply_trigger_t *trigger) { - printf("Greetings: %s\n", user_data); - printf("Puller : %s\n", data); + printf ("Greetings: %s\n", user_data); + printf ("Puller : %s\n", trigger_data); } - void main() + int + main (void) { ply_trigger_t *trigger = trigger_creator(); - trigger_puller(trigger); + + trigger_puller (trigger); + + return 0; } This program will print out @@ -297,14 +367,14 @@ This program will print out This shows how to transfer data from the different places to the handler. The general case is typically for user_data and data to be pointer to a struct containing multiple variables. Sometimes even -primitive data types (like +int+ or +bool+) are transfered to the +primitive data types (like +int+ or +bool+) are transferred to the handler by casting them to +void*+. This is certainly only possible if +sizeof(datatype) <= sizeof(void*)+. The handler +on_exit_handler+ expects +char*+ and is therefore not identical (but compatible) to the expected function type -+ply_trigger_t+. This is a very typical case and therefore we need the -type cast in +ply_trigger_add_handler+. ++ply_trigger_handler_t+. This is a very typical case and therefore +we need the cast in +ply_trigger_add_handler+. One advanced aspect of a trigger is its ignore counter. This allows a caller to ignore one or more pulls before really pulling the |