From 70c640489598775dfdc9c4f4de7c2b8e4498f355 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Thu, 31 Jan 2013 10:13:21 +0100 Subject: [PATCH 5/5] systemd-analyze: import systemctl dot Signed-off-by: Marc-Antoine Perennou --- TODO | 2 - man/systemctl.xml | 44 ------- man/systemd-analyze.xml | 107 +++++++++++---- shell-completion/systemd-bash-completion.sh | 2 +- shell-completion/systemd-zsh-completion.zsh | 9 +- src/analyze/systemd-analyze.c | 197 +++++++++++++++++++++++++++- src/systemctl/systemctl.c | 193 +-------------------------- 7 files changed, 284 insertions(+), 270 deletions(-) diff --git a/TODO b/TODO index 26e4c5a..d239db5 100644 --- a/TODO +++ b/TODO @@ -62,8 +62,6 @@ Features: * introduce ExecCondition= in services -* if we have systemd-analyze in C "systemctl dot" should move there too - * EFI: - fsck hookup for the ESP mount is missing - write man page for efi boot generator diff --git a/man/systemctl.xml b/man/systemctl.xml index 3842645..51792cf 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -272,32 +272,6 @@ - - - - When used in - conjunction with the - dot command (see - below), selects which dependencies are - shown in the dependency graph. If - is passed - only dependencies of type - After= or - Before= are - shown. If - is passed only dependencies of type - Requires=, - RequiresOverridable=, - Requisite=, - RequisiteOverridable=, - Wants= and - Conflicts= are - shown. If neither is passed, shows - dependencies of all these - types. - - - Don't send wall @@ -979,24 +953,6 @@ applications. - dot - - Generate textual - dependency graph description in dot - format for further processing with the - GraphViz - dot1 - tool. Use a command line like - systemctl dot | dot -Tsvg > - systemd.svg to generate a - graphical dependency tree. Unless - or - is passed - the generated graph will show both - ordering and requirement - dependencies. - - list-dependencies [NAME] Shows required and diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml index c2ff9cc..1e26fd8 100644 --- a/man/systemd-analyze.xml +++ b/man/systemd-analyze.xml @@ -66,30 +66,6 @@ to determine system boot-up performance of the current boot. - systemd-analyze time - prints the time spent in the kernel before - userspace has been reached, the time spent in the - initial RAM disk (initrd) before normal system - userspace has been reached and the time normal system - userspace took to initialize. Note that these - measurements simply measure the time passed up to the - point where all system services have been spawned, but - not necessarily until they fully finished - initialization or the disk is idle. - - systemd-analyze blame prints - a list of all running units, ordered by the time they - took to initialize. This information may be used to - optimize boot-up times. Note that the output might be - misleading as the initialization of one service might - be slow simply because it waits for the initialization - of another service to complete. - - systemd-analyze plot prints - an SVG graphic detailing which system services have - been started at what time, highlighting the time they - spent on initialization. - If no command is passed systemd-analyze time is implied. @@ -116,8 +92,91 @@ of user sessions instead of the system manager. + + + + + + When used in + conjunction with the + dot command (see + below), selects which dependencies are + shown in the dependency graph. If + is passed + only dependencies of type + After= or + Before= are + shown. If + is passed only dependencies of type + Requires=, + RequiresOverridable=, + Requisite=, + RequisiteOverridable=, + Wants= and + Conflicts= are + shown. If neither is passed, shows + dependencies of all these + types. + + The following commands are understood: + + + + time + + prints the time spent in the kernel before + userspace has been reached, the time spent in the + initial RAM disk (initrd) before normal system + userspace has been reached and the time normal system + userspace took to initialize. Note that these + measurements simply measure the time passed up to the + point where all system services have been spawned, but + not necessarily until they fully finished + initialization or the disk is idle. + + + + blame + + prints + a list of all running units, ordered by the time they + took to initialize. This information may be used to + optimize boot-up times. Note that the output might be + misleading as the initialization of one service might + be slow simply because it waits for the initialization + of another service to complete. + + + + blame + + prints + an SVG graphic detailing which system services have + been started at what time, highlighting the time they + spent on initialization. + + + + dot + + Generate textual + dependency graph description in dot + format for further processing with the + GraphViz + dot1 + tool. Use a command line like + systemd-analyze dot | dot -Tsvg > + systemd.svg to generate a + graphical dependency tree. Unless + or + is passed + the generated graph will show both + ordering and requirement + dependencies. + + diff --git a/shell-completion/systemd-bash-completion.sh b/shell-completion/systemd-bash-completion.sh index 52dc72b..1983a08 100644 --- a/shell-completion/systemd-bash-completion.sh +++ b/shell-completion/systemd-bash-completion.sh @@ -113,7 +113,7 @@ _systemctl () { [JOBS]='cancel' [SNAPSHOTS]='delete' [ENVS]='set-environment unset-environment' - [STANDALONE]='daemon-reexec daemon-reload default dot dump + [STANDALONE]='daemon-reexec daemon-reload default dump emergency exit halt hibernate hybrid-sleep kexec list-jobs list-units list-unit-files poweroff reboot rescue show-environment suspend' diff --git a/shell-completion/systemd-zsh-completion.zsh b/shell-completion/systemd-zsh-completion.zsh index d5fb850..488904b 100644 --- a/shell-completion/systemd-zsh-completion.zsh +++ b/shell-completion/systemd-zsh-completion.zsh @@ -27,8 +27,6 @@ _ctls() '--no-legend[Do not print a legend, i.e. the column headers and the footer with hints]' \ '--no-pager[Do not pipe output into a pager]' \ '--no-ask-password[Do not ask for system passwords]' \ - '--order[When generating graph for dot, show only order]' \ - '--require[When generating graph for dot, show only requirement]' \ '--system[Connect to system manager]' \ '--user[Connect to user service manager]' \ '--global[Enable/disable unit files globally]' \ @@ -135,7 +133,10 @@ _ctls() systemd-analyze) _arguments \ {-h,--help}'[Show help text.]' \ + {-v,--version}'[Show package version]' \ '--user[Shows performance data of user sessions instead of the system manager.]' \ + '--order[When generating graph for dot, show only order]' \ + '--require[When generating graph for dot, show only requirement]' \ '*::systemd-analyze commands:_systemd_analyze_command' ;; systemd-ask-password) @@ -293,6 +294,7 @@ _systemd_analyze_command(){ 'time:Print the time taken to start' 'blame:prints a list of all running units, ordered by the time they took to initialize' 'plot:prints an SVG graphic detailing which system services have been started at what time' + 'dot:Dump dependency graph for dot(1)' ) if (( CURRENT == 1 )); then @@ -352,7 +354,6 @@ _outputmodes() { "list-jobs:List jobs" "cancel:Cancel all, one, or more jobs" "dump:Dump server status" - "dot:Dump dependency graph for dot(1)" "snapshot:Create a snapshot" "delete:Remove one or more snapshots" "show-environment:Dump environment" @@ -575,7 +576,7 @@ done (( $+functions[_systemctl_link] )) || _systemctl_link() { _files } # no systemctl completion for: -# [STANDALONE]='daemon-reexec daemon-reload default dot dump +# [STANDALONE]='daemon-reexec daemon-reload default dump # emergency exit halt kexec list-jobs list-units # list-unit-files poweroff reboot rescue show-environment' # [NAME]='snapshot load' diff --git a/src/analyze/systemd-analyze.c b/src/analyze/systemd-analyze.c index 29fb418..c6bfb01 100644 --- a/src/analyze/systemd-analyze.c +++ b/src/analyze/systemd-analyze.c @@ -41,6 +41,11 @@ #include "dbus-common.h" static bool arg_user = false; +static enum dot { + DOT_ALL, + DOT_ORDER, + DOT_REQUIRE +} arg_dot = DOT_ALL; static int bus_get_uint64_property (DBusConnection *bus, const char *path, const char *interface, const char *property, uint64_t *val) { @@ -524,6 +529,176 @@ static int plot (DBusConnection *bus) { return 0; } +static int dot_one_property(const char *name, const char *prop, DBusMessageIter *iter) { + + static const char * const colors[] = { + "Requires", "[color=\"black\"]", + "RequiresOverridable", "[color=\"black\"]", + "Requisite", "[color=\"darkblue\"]", + "RequisiteOverridable", "[color=\"darkblue\"]", + "Wants", "[color=\"grey66\"]", + "Conflicts", "[color=\"red\"]", + "ConflictedBy", "[color=\"red\"]", + "After", "[color=\"green\"]" + }; + + const char *c = NULL; + unsigned i; + + assert(name); + assert(prop); + assert(iter); + + for (i = 0; i < ELEMENTSOF(colors); i += 2) + if (streq(colors[i], prop)) { + c = colors[i+1]; + break; + } + + if (!c) + return 0; + + if (arg_dot != DOT_ALL) + if ((arg_dot == DOT_ORDER) != streq(prop, "After")) + return 0; + + switch (dbus_message_iter_get_arg_type(iter)) { + + case DBUS_TYPE_ARRAY: + + if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) { + DBusMessageIter sub; + + dbus_message_iter_recurse(iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *s; + + assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING); + dbus_message_iter_get_basic(&sub, &s); + printf("\t\"%s\"->\"%s\" %s;\n", name, s, c); + + dbus_message_iter_next(&sub); + } + + return 0; + } + } + + return 0; +} + +static int dot_one(DBusConnection *bus, const struct unit_info *u) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + const char *interface = "org.freedesktop.systemd1.Unit"; + int r; + DBusMessageIter iter, sub, sub2, sub3; + + assert(bus); + assert(u); + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + u->unit_path, + "org.freedesktop.DBus.Properties", + "GetAll", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) { + log_error("Failed to parse reply."); + return -EIO; + } + + dbus_message_iter_recurse(&iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *prop; + + assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY); + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0 || + dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) { + log_error("Failed to parse reply."); + return -EIO; + } + + dbus_message_iter_recurse(&sub2, &sub3); + r = dot_one_property(u->id, prop, &sub3); + if (r < 0) + return r; + + dbus_message_iter_next(&sub); + } + + return 0; +} + +static int dot(DBusConnection *bus) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + DBusMessageIter iter, sub; + int r; + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListUnits", + &reply, + NULL, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) { + log_error("Failed to parse reply."); + return -EIO; + } + + printf("digraph systemd {\n"); + + dbus_message_iter_recurse(&iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + struct unit_info u; + + r = bus_parse_unit_info(&sub, &u); + if (r < 0) + return -EIO; + + r = dot_one(bus, &u); + if (r < 0) + return r; + + /* printf("\t\"%s\";\n", u.id); */ + dbus_message_iter_next(&sub); + } + + printf("}\n"); + + log_info(" Color legend: black = Requires\n" + " dark blue = Requisite\n" + " dark grey = Wants\n" + " red = Conflicts\n" + " green = After\n"); + + if (on_tty()) + log_notice("-- You probably want to process this output with graphviz' dot tool.\n" + "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n"); + + return 0; +} + static int help(void) { printf("usage: %s [-h] [-v] [--user] [{time,blame,plot}]\n\n" @@ -534,9 +709,12 @@ static int help(void) { " -h, --help show this help message and exit\n" " -v, --version show program's version number and exit\n" " --user use the session bus\n\n" - "time - print time spent in the kernel before reaching userspace\n" + " --order When generating graph for dot, show only order\n" + " --require When generating graph for dot, show only requirement\n" + "time - print time spent in the kernel before reaching userspace\n" "blame - print list of running units ordered by time to init\n" - "plot - output SVG graphic showing service initialization\n", + "plot - output SVG graphic showing service initialization\n" + "dot - Dump dependency graph for dot(1)\n", program_invocation_short_name); return 0; @@ -545,13 +723,17 @@ static int help(void) { static int parse_argv(int argc, char *argv[]) { enum { - ARG_USER + ARG_USER, + ARG_ORDER, + ARG_REQUIRE }; static const struct option options[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'v' }, { "user", no_argument, NULL, ARG_USER }, + { "order", no_argument, NULL, ARG_ORDER }, + { "require", no_argument, NULL, ARG_REQUIRE }, { NULL, 0, NULL, 0 } }; @@ -574,6 +756,14 @@ static int parse_argv(int argc, char *argv[]) { arg_user = true; break; + case ARG_ORDER: + arg_dot = DOT_ORDER; + break; + + case ARG_REQUIRE: + arg_dot = DOT_REQUIRE; + break; + } } @@ -595,6 +785,7 @@ int main(int argc, char*argv[]) { { "time", LESS, 1, _time }, { "blame", LESS, 1, blame }, { "plot", LESS, 1, plot }, + { "dot", EQUAL, 1, dot }, }; unsigned i; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 54c8ebc..3f8afbc 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -116,11 +116,6 @@ static enum action { ACTION_CANCEL_SHUTDOWN, _ACTION_MAX } arg_action = ACTION_SYSTEMCTL; -static enum dot { - DOT_ALL, - DOT_ORDER, - DOT_REQUIRE -} arg_dot = DOT_ALL; static enum transport { TRANSPORT_NORMAL, TRANSPORT_SSH, @@ -891,176 +886,6 @@ static int list_dependencies(DBusConnection *bus, char **args) { return list_dependencies_one(bus, u, 0, NULL, 0); } -static int dot_one_property(const char *name, const char *prop, DBusMessageIter *iter) { - - static const char * const colors[] = { - "Requires", "[color=\"black\"]", - "RequiresOverridable", "[color=\"black\"]", - "Requisite", "[color=\"darkblue\"]", - "RequisiteOverridable", "[color=\"darkblue\"]", - "Wants", "[color=\"grey66\"]", - "Conflicts", "[color=\"red\"]", - "ConflictedBy", "[color=\"red\"]", - "After", "[color=\"green\"]" - }; - - const char *c = NULL; - unsigned i; - - assert(name); - assert(prop); - assert(iter); - - for (i = 0; i < ELEMENTSOF(colors); i += 2) - if (streq(colors[i], prop)) { - c = colors[i+1]; - break; - } - - if (!c) - return 0; - - if (arg_dot != DOT_ALL) - if ((arg_dot == DOT_ORDER) != streq(prop, "After")) - return 0; - - switch (dbus_message_iter_get_arg_type(iter)) { - - case DBUS_TYPE_ARRAY: - - if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) { - DBusMessageIter sub; - - dbus_message_iter_recurse(iter, &sub); - - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - const char *s; - - assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING); - dbus_message_iter_get_basic(&sub, &s); - printf("\t\"%s\"->\"%s\" %s;\n", name, s, c); - - dbus_message_iter_next(&sub); - } - - return 0; - } - } - - return 0; -} - -static int dot_one(DBusConnection *bus, const struct unit_info *u) { - _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; - const char *interface = "org.freedesktop.systemd1.Unit"; - int r; - DBusMessageIter iter, sub, sub2, sub3; - - assert(bus); - assert(u); - - r = bus_method_call_with_reply( - bus, - "org.freedesktop.systemd1", - u->unit_path, - "org.freedesktop.DBus.Properties", - "GetAll", - &reply, - NULL, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_INVALID); - if (r < 0) - return r; - - if (!dbus_message_iter_init(reply, &iter) || - dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || - dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) { - log_error("Failed to parse reply."); - return -EIO; - } - - dbus_message_iter_recurse(&iter, &sub); - - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - const char *prop; - - assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY); - dbus_message_iter_recurse(&sub, &sub2); - - if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0 || - dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) { - log_error("Failed to parse reply."); - return -EIO; - } - - dbus_message_iter_recurse(&sub2, &sub3); - r = dot_one_property(u->id, prop, &sub3); - if (r < 0) - return r; - - dbus_message_iter_next(&sub); - } - - return 0; -} - -static int dot(DBusConnection *bus, char **args) { - _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; - DBusMessageIter iter, sub; - int r; - - r = bus_method_call_with_reply( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnits", - &reply, - NULL, - DBUS_TYPE_INVALID); - if (r < 0) - return r; - - if (!dbus_message_iter_init(reply, &iter) || - dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || - dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) { - log_error("Failed to parse reply."); - return -EIO; - } - - printf("digraph systemd {\n"); - - dbus_message_iter_recurse(&iter, &sub); - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - struct unit_info u; - - r = bus_parse_unit_info(&sub, &u); - if (r < 0) - return -EIO; - - r = dot_one(bus, &u); - if (r < 0) - return r; - - /* printf("\t\"%s\";\n", u.id); */ - dbus_message_iter_next(&sub); - } - - printf("}\n"); - - log_info(" Color legend: black = Requires\n" - " dark blue = Requisite\n" - " dark grey = Wants\n" - " red = Conflicts\n" - " green = After\n"); - - if (on_tty()) - log_notice("-- You probably want to process this output with graphviz' dot tool.\n" - "-- Try a shell pipeline like 'systemctl dot | dot -Tsvg > systemd.svg'!\n"); - - return 0; -} - static int list_jobs(DBusConnection *bus, char **args) { _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; DBusMessageIter iter, sub, sub2; @@ -4276,8 +4101,6 @@ static int systemctl_help(void) { " --no-pager Do not pipe output into a pager\n" " --no-ask-password\n" " Do not ask for system passwords\n" - " --order When generating graph for dot, show only order\n" - " --require When generating graph for dot, show only requirement\n" " --system Connect to system manager\n" " --user Connect to user service manager\n" " --global Enable/disable unit files globally\n" @@ -4336,8 +4159,7 @@ static int systemctl_help(void) { " list-jobs List jobs\n" " cancel [JOB...] Cancel all, one, or more jobs\n\n" "Status Commands:\n" - " dump Dump server status\n" - " dot Dump dependency graph for dot(1)\n\n" + " dump Dump server status\n\n" "Snapshot Commands:\n" " snapshot [NAME] Create a snapshot\n" " delete [NAME...] Remove one or more snapshots\n\n" @@ -4460,8 +4282,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_NO_LEGEND, ARG_NO_PAGER, ARG_NO_WALL, - ARG_ORDER, - ARG_REQUIRE, ARG_ROOT, ARG_FULL, ARG_NO_RELOAD, @@ -4491,8 +4311,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "no-pager", no_argument, NULL, ARG_NO_PAGER }, { "no-wall", no_argument, NULL, ARG_NO_WALL }, { "quiet", no_argument, NULL, 'q' }, - { "order", no_argument, NULL, ARG_ORDER }, - { "require", no_argument, NULL, ARG_REQUIRE }, { "root", required_argument, NULL, ARG_ROOT }, { "force", no_argument, NULL, ARG_FORCE }, { "no-reload", no_argument, NULL, ARG_NO_RELOAD }, @@ -4599,14 +4417,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_no_wall = true; break; - case ARG_ORDER: - arg_dot = DOT_ORDER; - break; - - case ARG_REQUIRE: - arg_dot = DOT_REQUIRE; - break; - case ARG_ROOT: arg_root = optarg; break; @@ -5307,7 +5117,6 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "status", MORE, 2, show }, { "help", MORE, 2, show }, { "dump", EQUAL, 1, dump }, - { "dot", EQUAL, 1, dot }, { "snapshot", LESS, 2, snapshot }, { "delete", MORE, 2, delete_snapshot }, { "daemon-reload", EQUAL, 1, daemon_reload }, -- 1.8.1.1