mirror of
https://github.com/systemd/systemd.git
synced 2024-12-23 21:35:11 +03:00
Merge pull request #2664 from zonque/bootchart-removal
Remove systemd-bootchart
This commit is contained in:
commit
c550f7a9b8
1
.gitignore
vendored
1
.gitignore
vendored
@ -53,7 +53,6 @@
|
||||
/systemd-ask-password
|
||||
/systemd-backlight
|
||||
/systemd-binfmt
|
||||
/systemd-bootchart
|
||||
/systemd-bootx64.efi
|
||||
/systemd-cat
|
||||
/systemd-cgls
|
||||
|
@ -1843,18 +1843,6 @@ man/systemd-binfmt.html: man/systemd-binfmt.service.html
|
||||
|
||||
endif
|
||||
|
||||
if ENABLE_BOOTCHART
|
||||
MANPAGES += \
|
||||
man/bootchart.conf.5 \
|
||||
man/systemd-bootchart.1
|
||||
MANPAGES_ALIAS += \
|
||||
man/bootchart.conf.d.5
|
||||
man/bootchart.conf.d.5: man/bootchart.conf.5
|
||||
man/bootchart.conf.d.html: man/bootchart.conf.html
|
||||
$(html-alias)
|
||||
|
||||
endif
|
||||
|
||||
if ENABLE_COREDUMP
|
||||
MANPAGES += \
|
||||
man/coredump.conf.5 \
|
||||
@ -2460,7 +2448,6 @@ endif
|
||||
|
||||
EXTRA_DIST += \
|
||||
man/binfmt.d.xml \
|
||||
man/bootchart.conf.xml \
|
||||
man/bootctl.xml \
|
||||
man/bootup.xml \
|
||||
man/busctl.xml \
|
||||
@ -2583,7 +2570,6 @@ EXTRA_DIST += \
|
||||
man/systemd-ask-password.xml \
|
||||
man/systemd-backlight@.service.xml \
|
||||
man/systemd-binfmt.service.xml \
|
||||
man/systemd-bootchart.xml \
|
||||
man/systemd-cat.xml \
|
||||
man/systemd-cgls.xml \
|
||||
man/systemd-cgtop.xml \
|
||||
|
26
Makefile.am
26
Makefile.am
@ -4415,32 +4415,6 @@ EXTRA_DIST += \
|
||||
src/vconsole/90-vconsole.rules.in \
|
||||
units/systemd-vconsole-setup.service.in
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
if ENABLE_BOOTCHART
|
||||
systemd_bootchart_SOURCES = \
|
||||
src/bootchart/bootchart.c \
|
||||
src/bootchart/bootchart.h \
|
||||
src/bootchart/store.c \
|
||||
src/bootchart/store.h \
|
||||
src/bootchart/svg.c \
|
||||
src/bootchart/svg.h
|
||||
|
||||
systemd_bootchart_LDADD = \
|
||||
libshared.la
|
||||
|
||||
rootlibexec_PROGRAMS += \
|
||||
systemd-bootchart
|
||||
|
||||
dist_pkgsysconf_DATA += \
|
||||
src/bootchart/bootchart.conf
|
||||
|
||||
nodist_systemunit_DATA += \
|
||||
units/systemd-bootchart.service
|
||||
endif
|
||||
|
||||
EXTRA_DIST += \
|
||||
units/systemd-bootchart.service.in
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
if ENABLE_QUOTACHECK
|
||||
rootlibexec_PROGRAMS += \
|
||||
|
4
README
4
README
@ -88,10 +88,6 @@ REQUIREMENTS:
|
||||
Required for CPUQuota= in resource control unit settings
|
||||
CONFIG_CFS_BANDWIDTH
|
||||
|
||||
For systemd-bootchart, several proc debug interfaces are required:
|
||||
CONFIG_SCHEDSTATS
|
||||
CONFIG_SCHED_DEBUG
|
||||
|
||||
For UEFI systems:
|
||||
CONFIG_EFIVAR_FS
|
||||
CONFIG_EFI_PARTITION
|
||||
|
6
TODO
6
TODO
@ -754,12 +754,6 @@ Features:
|
||||
works with ^C
|
||||
- add documentation to systemd.daemon
|
||||
|
||||
* bootchart:
|
||||
- plot per-process IO utilization
|
||||
- group processes based on service association (cgroups)
|
||||
- document initcall_debug
|
||||
- kernel cmdline "bootchart" option for simplicity?
|
||||
|
||||
* udev-link-config:
|
||||
- Make sure ID_PATH is always exported and complete for
|
||||
network devices where possible, so we can safely rely
|
||||
|
@ -938,14 +938,6 @@ if test "x$enable_vconsole" != "xno"; then
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_VCONSOLE, [test "$have_vconsole" = "yes"])
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
have_bootchart=no
|
||||
AC_ARG_ENABLE(bootchart, AS_HELP_STRING([--disable-bootchart], [disable bootchart tool]))
|
||||
if test "x$enable_bootchart" != "xno"; then
|
||||
have_bootchart=yes
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_BOOTCHART, [test "$have_bootchart" = "yes"])
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
have_quotacheck=no
|
||||
AC_ARG_ENABLE(quotacheck, AS_HELP_STRING([--disable-quotacheck], [disable quotacheck tools]))
|
||||
@ -1571,7 +1563,6 @@ AC_MSG_RESULT([
|
||||
ELFUTILS: ${have_elfutils}
|
||||
binfmt: ${have_binfmt}
|
||||
vconsole: ${have_vconsole}
|
||||
bootchart: ${have_bootchart}
|
||||
quotacheck: ${have_quotacheck}
|
||||
tmpfiles: ${have_tmpfiles}
|
||||
sysusers: ${have_sysusers}
|
||||
|
@ -1,172 +0,0 @@
|
||||
<?xml version='1.0'?> <!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
|
||||
<!--
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2012 Intel Corporation
|
||||
|
||||
Authors:
|
||||
Auke Kok <auke-jan.h.kok@intel.com>
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<refentry id="bootchart.conf" conditional='ENABLE_BOOTCHART'
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<refentryinfo>
|
||||
<title>bootchart.conf</title>
|
||||
<productname>systemd</productname>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
<contrib>Developer</contrib>
|
||||
<firstname>Auke</firstname>
|
||||
<surname>Kok</surname>
|
||||
<email>auke-jan.h.kok@intel.com</email>
|
||||
</author>
|
||||
</authorgroup>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>bootchart.conf</refentrytitle>
|
||||
<manvolnum>5</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>bootchart.conf</refname>
|
||||
<refname>bootchart.conf.d</refname>
|
||||
<refpurpose>Boot performance analysis graphing tool configuration files</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<para><filename>/etc/systemd/bootchart.conf</filename></para>
|
||||
<para><filename>/etc/systemd/bootchart.conf.d/*.conf</filename></para>
|
||||
<para><filename>/run/systemd/bootchart.conf.d/*.conf</filename></para>
|
||||
<para><filename>/usr/lib/systemd/bootchart.conf.d/*.conf</filename></para>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>When starting, systemd-bootchart will read the configuration
|
||||
file <filename>/etc/systemd/bootchart.conf</filename>, followed by
|
||||
the files in the <filename>bootchart.conf.d</filename>
|
||||
directories. These configuration files determine logging
|
||||
parameters and graph output.</para>
|
||||
</refsect1>
|
||||
|
||||
<xi:include href="standard-conf.xml" xpointer="main-conf" />
|
||||
|
||||
<refsect1>
|
||||
<title>Options</title>
|
||||
|
||||
<variablelist class='bootchart-directives'>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Samples=500</varname></term>
|
||||
<listitem><para>Configure the amount of samples to record in
|
||||
total before bootchart exits. Each sample will record at
|
||||
intervals defined by Frequency=.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Frequency=25</varname></term>
|
||||
<listitem><para>Configure the sample log frequency. This can
|
||||
be a fractional number, but must be larger than 0.0. Most
|
||||
systems can cope with values under 25–50 without impacting
|
||||
boot time severely.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Relative=no</varname></term>
|
||||
<listitem><para>Configures whether the left axis of the output
|
||||
graph equals time=0.0 (<constant>CLOCK_MONOTONIC</constant>
|
||||
start). This is useful for using bootchart at post-boot time
|
||||
to profile an already booted system, otherwise the graph would
|
||||
become extremely large. If set to yes, the horizontal axis
|
||||
starts at the first recorded sample instead of time=0.0.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Filter=no</varname></term>
|
||||
<listitem><para>Configures whether the resulting graph should
|
||||
omit tasks that did not contribute significantly to the boot.
|
||||
Processes that are too short-lived (only seen in one sample)
|
||||
or that do not consume any significant CPU time (less than
|
||||
0.001sec) will not be displayed in the output
|
||||
graph.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Output=[path]</varname></term>
|
||||
<listitem><para>Configures the output directory for writing
|
||||
the graphs. By default, bootchart writes the graphs to
|
||||
<filename>/run/log</filename>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Init=[path]</varname></term>
|
||||
<listitem><para>Configures bootchart to run a non-standard
|
||||
binary instead of
|
||||
<filename>/usr/lib/systemd/systemd</filename>. This option is
|
||||
only relevant if bootchart was invoked from the kernel command
|
||||
line with
|
||||
init=/usr/lib/systemd/systemd-bootchart.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>PlotMemoryUsage=no</varname></term>
|
||||
<listitem><para>If set to yes, enables logging and graphing of
|
||||
processes' PSS memory consumption.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>PlotEntropyGraph=no</varname></term>
|
||||
<listitem><para>If set to yes, enables logging and graphing of
|
||||
the kernel random entropy pool size.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ScaleX=100</varname></term>
|
||||
<listitem><para>Horizontal scaling factor for all variable
|
||||
graph components.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ScaleY=20</varname></term>
|
||||
<listitem><para>Vertical scaling factor for all variable graph
|
||||
components.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ControlGroup=no</varname></term>
|
||||
<listitem><para>Display process control group.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para>
|
||||
<citerefentry><refentrytitle>systemd-bootchart</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd.directives</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
@ -1,323 +0,0 @@
|
||||
<?xml version='1.0'?> <!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
|
||||
<!--
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2012 Intel Corporation
|
||||
|
||||
Authors:
|
||||
Auke Kok <auke-jan.h.kok@intel.com>
|
||||
William Giokas <1007380@gmail.com>
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<refentry id="systemd-bootchart" conditional='ENABLE_BOOTCHART'
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-bootchart</title>
|
||||
<productname>systemd</productname>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
<contrib>Developer</contrib>
|
||||
<firstname>Auke</firstname>
|
||||
<surname>Kok</surname>
|
||||
<email>auke-jan.h.kok@intel.com</email>
|
||||
</author>
|
||||
</authorgroup>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-bootchart</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-bootchart</refname>
|
||||
<refpurpose>Boot performance graphing tool</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
<para>
|
||||
<command>systemd-bootchart</command> is a tool, usually run at
|
||||
system startup, that collects the CPU load, disk load, memory
|
||||
usage, as well as per-process information from a running system.
|
||||
Collected results are output as an SVG graph. Normally,
|
||||
systemd-bootchart is invoked by the kernel by passing
|
||||
<option>init=<filename>/usr/lib/systemd/systemd-bootchart</filename></option>
|
||||
on the kernel command line. systemd-bootchart will then fork the
|
||||
real init off to resume normal system startup, while monitoring
|
||||
and logging startup information in the background.
|
||||
</para>
|
||||
<para>
|
||||
After collecting a certain amount of data (usually 15–30
|
||||
seconds, default 20 s) the logging stops and a graph is
|
||||
generated from the logged information. This graph contains vital
|
||||
clues as to which resources are being used, in which order, and
|
||||
where possible problems exist in the startup sequence of the
|
||||
system. It is essentially a more detailed version of the
|
||||
<command>systemd-analyze plot</command> function.
|
||||
</para>
|
||||
<para>
|
||||
Of course, bootchart can also be used at any moment in time to
|
||||
collect and graph some data for an amount of time. It is
|
||||
recommended to use the <option>--rel</option> switch in this
|
||||
case.
|
||||
</para>
|
||||
<para>
|
||||
Bootchart does not require root privileges, and will happily run
|
||||
as a normal user.
|
||||
</para>
|
||||
<para>
|
||||
Bootchart graphs are by default written time-stamped in
|
||||
<filename>/run/log</filename> and saved to the journal with
|
||||
<varname>MESSAGE_ID=9f26aa562cf440c2b16c773d0479b518</varname>.
|
||||
Journal field <varname>BOOTCHART=</varname> contains the
|
||||
bootchart in SVG format.
|
||||
</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Invocation</title>
|
||||
|
||||
<para><command>systemd-bootchart</command> can be invoked in several different ways:</para>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry>
|
||||
<term><emphasis>Kernel invocation</emphasis></term>
|
||||
<listitem><para>The kernel can invoke
|
||||
<command>systemd-bootchart</command> instead of the init
|
||||
process. In turn, <command>systemd-bootchart</command> will
|
||||
invoke <command>/usr/lib/systemd/systemd</command>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><emphasis>Started as a standalone program</emphasis></term>
|
||||
<listitem><para>One can execute
|
||||
<command>systemd-bootchart</command> as normal application
|
||||
from the command line. In this mode, it is highly recommended
|
||||
to pass the <option>-r</option> flag in order to not graph the
|
||||
time elapsed since boot and before systemd-bootchart was
|
||||
started, as it may result in extremely large graphs. The time
|
||||
elapsed since boot might also include any time that the system
|
||||
was suspended.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Options</title>
|
||||
|
||||
<para>These options can also be set in the
|
||||
<filename>/etc/systemd/bootchart.conf</filename> file. See
|
||||
<citerefentry project='man-pages'><refentrytitle>bootchart.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
||||
</para>
|
||||
|
||||
<variablelist>
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-n</option></term>
|
||||
<term><option>--sample <replaceable>N</replaceable></option></term>
|
||||
<listitem><para>Specify the number of samples,
|
||||
<replaceable>N</replaceable>, to record. Samples will be
|
||||
recorded at intervals defined with <option>--freq</option>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-f</option></term>
|
||||
<term><option>--freq <replaceable>f</replaceable></option></term>
|
||||
<listitem><para>Specify the sample log frequency, a positive
|
||||
real <replaceable>f</replaceable>, in Hz. Most systems can
|
||||
cope with values up to 25–50 without creating too much
|
||||
overhead.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-r</option></term>
|
||||
<term><option>--rel</option></term>
|
||||
<listitem><para>Use relative times instead of absolute times.
|
||||
This is useful for using bootchart at post-boot time to
|
||||
profile an already booted system. Without this option the
|
||||
graph would become extremely large. If set, the horizontal
|
||||
axis starts at the first recorded sample instead of time
|
||||
0.0.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-F</option></term>
|
||||
<term><option>--no-filter</option></term>
|
||||
<listitem><para>Disable filtering of tasks that did not
|
||||
contribute significantly to the boot. Processes that are too
|
||||
short-lived (only seen in one sample) or that do not consume
|
||||
any significant CPU time (less than 0.001 s) will not be
|
||||
displayed in the output graph. </para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-C</option></term>
|
||||
<term><option>--cmdline</option></term>
|
||||
<listitem><para>Display the full command line with arguments
|
||||
of processes, instead of only the process name.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-g</option></term>
|
||||
<term><option>--control-group</option></term>
|
||||
<listitem><para>Display process control group
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-o</option></term>
|
||||
<term><option>--output <replaceable>path</replaceable></option></term>
|
||||
<listitem><para>Specify the output directory for the graphs.
|
||||
By default, bootchart writes the graphs to
|
||||
<filename>/run/log</filename>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-i</option></term>
|
||||
<term><option>--init <replaceable>path</replaceable></option></term>
|
||||
<listitem><para>Use this init binary. Defaults to
|
||||
<command>/usr/lib/systemd/systemd</command>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-p</option></term>
|
||||
<term><option>--pss</option></term>
|
||||
<listitem><para>Enable logging and graphing of processes' PSS
|
||||
(Proportional Set Size) memory consumption. See
|
||||
<filename>filesystems/proc.txt</filename> in the kernel
|
||||
documentation for an explanation of this field.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-e</option></term>
|
||||
<term><option>--entropy</option></term>
|
||||
<listitem><para>Enable logging and graphing of the kernel
|
||||
random entropy pool size.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-x</option></term>
|
||||
<term><option>--scale-x <replaceable>N</replaceable></option></term>
|
||||
<listitem><para>Horizontal scaling factor for all variable
|
||||
graph components.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-y</option></term>
|
||||
<term><option>--scale-y <replaceable>N</replaceable></option></term>
|
||||
<listitem><para>Vertical scaling factor for all variable graph
|
||||
components.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Output</title>
|
||||
|
||||
<para><command>systemd-bootchart</command> generates SVG graphs.
|
||||
In order to render those on a graphical display any SVG capable
|
||||
viewer can be used. It should be noted that the SVG render engines
|
||||
in most browsers (including Chrome and Firefox) are many times
|
||||
faster than dedicated graphical applications like Gimp and
|
||||
Inkscape. Just point your browser at
|
||||
<ulink url="file:///run/log/" />!
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>History</title>
|
||||
|
||||
<para>This version of bootchart was implemented from scratch, but
|
||||
is inspired by former bootchart incantations:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><emphasis>Original bash</emphasis></term>
|
||||
<listitem><para>The original bash/shell code implemented
|
||||
bootchart. This version created a compressed tarball for
|
||||
processing with external applications. This version did not
|
||||
graph anything, only generated data.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><emphasis>Ubuntu C Implementation</emphasis></term>
|
||||
<listitem><para>This version replaced the shell version with a
|
||||
fast and efficient data logger, but also did not graph the
|
||||
data.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><emphasis>Java bootchart</emphasis></term>
|
||||
<listitem><para>This was the original graphing application for
|
||||
charting the data, written in java.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><emphasis>pybootchartgui.py</emphasis></term>
|
||||
<listitem><para>pybootchart created a graph from the data
|
||||
collected by either the bash or C version.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<para>The version of bootchart you are using now combines both the
|
||||
data collection and the charting into a single application, making
|
||||
it more efficient and simpler. There are no longer any timing
|
||||
issues with the data collector and the grapher, as the graphing
|
||||
cannot be run until the data has been collected. Also, the data
|
||||
kept in memory is reduced to the absolute minimum needed.</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
|
||||
<para>
|
||||
<citerefentry project='man-pages'><refentrytitle>bootchart.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Bugs</title>
|
||||
|
||||
<para>systemd-bootchart does not get the model information for the
|
||||
hard drive unless the root device is specified with
|
||||
<code>root=/dev/sdxY</code>. Using UUIDs or PARTUUIDs will boot
|
||||
fine, but the hard drive model will not be added to the
|
||||
chart.</para>
|
||||
<para>For bugs, please contact the author and current maintainer:</para>
|
||||
<simplelist>
|
||||
<member>Auke Kok <email>auke-jan.h.kok@intel.com</email></member>
|
||||
</simplelist>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
@ -77,12 +77,10 @@ int uname_architecture(void);
|
||||
#if defined(__x86_64__)
|
||||
# define native_architecture() ARCHITECTURE_X86_64
|
||||
# define LIB_ARCH_TUPLE "x86_64-linux-gnu"
|
||||
# define PROC_CPUINFO_MODEL "model name"
|
||||
# define SECONDARY_ARCHITECTURE ARCHITECTURE_X86
|
||||
#elif defined(__i386__)
|
||||
# define native_architecture() ARCHITECTURE_X86
|
||||
# define LIB_ARCH_TUPLE "i386-linux-gnu"
|
||||
# define PROC_CPUINFO_MODEL "model name"
|
||||
#elif defined(__powerpc64__)
|
||||
# if __BYTE_ORDER == __BIG_ENDIAN
|
||||
# define native_architecture() ARCHITECTURE_PPC64
|
||||
@ -93,7 +91,6 @@ int uname_architecture(void);
|
||||
# define LIB_ARCH_TUPLE "powerpc64le-linux-gnu"
|
||||
# define SECONDARY_ARCHITECTURE ARCHITECTURE_PPC_LE
|
||||
# endif
|
||||
# define PROC_CPUINFO_MODEL "cpu"
|
||||
#elif defined(__powerpc__)
|
||||
# if __BYTE_ORDER == __BIG_ENDIAN
|
||||
# define native_architecture() ARCHITECTURE_PPC
|
||||
@ -102,18 +99,15 @@ int uname_architecture(void);
|
||||
# define native_architecture() ARCHITECTURE_PPC_LE
|
||||
# error "Missing LIB_ARCH_TUPLE for PPCLE"
|
||||
# endif
|
||||
# define PROC_CPUINFO_MODEL "cpu"
|
||||
#elif defined(__ia64__)
|
||||
# define native_architecture() ARCHITECTURE_IA64
|
||||
# define LIB_ARCH_TUPLE "ia64-linux-gnu"
|
||||
#elif defined(__hppa64__)
|
||||
# define native_architecture() ARCHITECTURE_PARISC64
|
||||
# error "Missing LIB_ARCH_TUPLE for HPPA64"
|
||||
# define PROC_CPUINFO_MODEL "cpu"
|
||||
#elif defined(__hppa__)
|
||||
# define native_architecture() ARCHITECTURE_PARISC
|
||||
# define LIB_ARCH_TUPLE "hppa‑linux‑gnu"
|
||||
# define PROC_CPUINFO_MODEL "cpu"
|
||||
#elif defined(__s390x__)
|
||||
# define native_architecture() ARCHITECTURE_S390X
|
||||
# define LIB_ARCH_TUPLE "s390x-linux-gnu"
|
||||
@ -124,11 +118,9 @@ int uname_architecture(void);
|
||||
#elif defined(__sparc64__)
|
||||
# define native_architecture() ARCHITECTURE_SPARC64
|
||||
# define LIB_ARCH_TUPLE "sparc64-linux-gnu"
|
||||
# define PROC_CPUINFO_MODEL "cpu"
|
||||
#elif defined(__sparc__)
|
||||
# define native_architecture() ARCHITECTURE_SPARC
|
||||
# define LIB_ARCH_TUPLE "sparc-linux-gnu"
|
||||
# define PROC_CPUINFO_MODEL "cpu"
|
||||
#elif defined(__mips64__)
|
||||
# if __BYTE_ORDER == __BIG_ENDIAN
|
||||
# define native_architecture() ARCHITECTURE_MIPS64
|
||||
@ -137,7 +129,6 @@ int uname_architecture(void);
|
||||
# define native_architecture() ARCHITECTURE_MIPS64_LE
|
||||
# error "Missing LIB_ARCH_TUPLE for MIPS64_LE"
|
||||
# endif
|
||||
# define PROC_CPUINFO_MODEL "cpu model"
|
||||
#elif defined(__mips__)
|
||||
# if __BYTE_ORDER == __BIG_ENDIAN
|
||||
# define native_architecture() ARCHITECTURE_MIPS
|
||||
@ -146,7 +137,6 @@ int uname_architecture(void);
|
||||
# define native_architecture() ARCHITECTURE_MIPS_LE
|
||||
# define LIB_ARCH_TUPLE "mipsel-linux-gnu"
|
||||
# endif
|
||||
# define PROC_CPUINFO_MODEL "cpu model"
|
||||
#elif defined(__alpha__)
|
||||
# define native_architecture() ARCHITECTURE_ALPHA
|
||||
# define LIB_ARCH_TUPLE "alpha-linux-gnu"
|
||||
@ -182,7 +172,6 @@ int uname_architecture(void);
|
||||
# define LIB_ARCH_TUPLE "arm-linux-gnu"
|
||||
# endif
|
||||
# endif
|
||||
# define PROC_CPUINFO_MODEL "model name"
|
||||
#elif defined(__sh64__)
|
||||
# define native_architecture() ARCHITECTURE_SH64
|
||||
# error "Missing LIB_ARCH_TUPLE for SH64"
|
||||
@ -202,10 +191,5 @@ int uname_architecture(void);
|
||||
# error "Please register your architecture here!"
|
||||
#endif
|
||||
|
||||
#ifndef PROC_CPUINFO_MODEL
|
||||
#warning "PROC_CPUINFO_MODEL not defined for your architecture"
|
||||
#define PROC_CPUINFO_MODEL "model name"
|
||||
#endif
|
||||
|
||||
const char *architecture_to_string(int a) _const_;
|
||||
int architecture_from_string(const char *s) _pure_;
|
||||
|
@ -1 +0,0 @@
|
||||
../Makefile
|
@ -1,531 +0,0 @@
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright (C) 2009-2013 Intel Corporation
|
||||
|
||||
Authors:
|
||||
Auke Kok <auke-jan.h.kok@intel.com>
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
/***
|
||||
|
||||
Many thanks to those who contributed ideas and code:
|
||||
- Ziga Mahkovec - Original bootchart author
|
||||
- Anders Norgaard - PyBootchartgui
|
||||
- Michael Meeks - bootchart2
|
||||
- Scott James Remnant - Ubuntu C-based logger
|
||||
- Arjan van der Ven - for the idea to merge bootgraph.pl functionality
|
||||
|
||||
***/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sd-journal.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bootchart.h"
|
||||
#include "conf-parser.h"
|
||||
#include "def.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "io-util.h"
|
||||
#include "list.h"
|
||||
#include "macro.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "store.h"
|
||||
#include "string-util.h"
|
||||
#include "strxcpyx.h"
|
||||
#include "svg.h"
|
||||
#include "util.h"
|
||||
|
||||
static int exiting = 0;
|
||||
|
||||
#define DEFAULT_SAMPLES_LEN 500
|
||||
#define DEFAULT_HZ 25.0
|
||||
#define DEFAULT_SCALE_X 100.0 /* 100px = 1sec */
|
||||
#define DEFAULT_SCALE_Y 20.0 /* 16px = 1 process bar */
|
||||
#define DEFAULT_INIT ROOTLIBEXECDIR "/systemd"
|
||||
#define DEFAULT_OUTPUT "/run/log"
|
||||
|
||||
/* graph defaults */
|
||||
bool arg_entropy = false;
|
||||
bool arg_initcall = true;
|
||||
bool arg_relative = false;
|
||||
bool arg_filter = true;
|
||||
bool arg_show_cmdline = false;
|
||||
bool arg_show_cgroup = false;
|
||||
bool arg_pss = false;
|
||||
bool arg_percpu = false;
|
||||
int arg_samples_len = DEFAULT_SAMPLES_LEN; /* we record len+1 (1 start sample) */
|
||||
double arg_hz = DEFAULT_HZ;
|
||||
double arg_scale_x = DEFAULT_SCALE_X;
|
||||
double arg_scale_y = DEFAULT_SCALE_Y;
|
||||
|
||||
char arg_init_path[PATH_MAX] = DEFAULT_INIT;
|
||||
char arg_output_path[PATH_MAX] = DEFAULT_OUTPUT;
|
||||
|
||||
static void signal_handler(int sig) {
|
||||
exiting = 1;
|
||||
}
|
||||
|
||||
#define BOOTCHART_MAX (16*1024*1024)
|
||||
|
||||
static void parse_conf(void) {
|
||||
char *init = NULL, *output = NULL;
|
||||
const ConfigTableItem items[] = {
|
||||
{ "Bootchart", "Samples", config_parse_int, 0, &arg_samples_len },
|
||||
{ "Bootchart", "Frequency", config_parse_double, 0, &arg_hz },
|
||||
{ "Bootchart", "Relative", config_parse_bool, 0, &arg_relative },
|
||||
{ "Bootchart", "Filter", config_parse_bool, 0, &arg_filter },
|
||||
{ "Bootchart", "Output", config_parse_path, 0, &output },
|
||||
{ "Bootchart", "Init", config_parse_path, 0, &init },
|
||||
{ "Bootchart", "PlotMemoryUsage", config_parse_bool, 0, &arg_pss },
|
||||
{ "Bootchart", "PlotEntropyGraph", config_parse_bool, 0, &arg_entropy },
|
||||
{ "Bootchart", "ScaleX", config_parse_double, 0, &arg_scale_x },
|
||||
{ "Bootchart", "ScaleY", config_parse_double, 0, &arg_scale_y },
|
||||
{ "Bootchart", "ControlGroup", config_parse_bool, 0, &arg_show_cgroup },
|
||||
{ "Bootchart", "PerCPU", config_parse_bool, 0, &arg_percpu },
|
||||
{ NULL, NULL, NULL, 0, NULL }
|
||||
};
|
||||
|
||||
config_parse_many(PKGSYSCONFDIR "/bootchart.conf",
|
||||
CONF_PATHS_NULSTR("systemd/bootchart.conf.d"),
|
||||
NULL, config_item_table_lookup, items, true, NULL);
|
||||
|
||||
if (init != NULL)
|
||||
strscpy(arg_init_path, sizeof(arg_init_path), init);
|
||||
if (output != NULL)
|
||||
strscpy(arg_output_path, sizeof(arg_output_path), output);
|
||||
}
|
||||
|
||||
static void help(void) {
|
||||
printf("Usage: %s [OPTIONS]\n\n"
|
||||
"Options:\n"
|
||||
" -r --rel Record time relative to recording\n"
|
||||
" -f --freq=FREQ Sample frequency [%g]\n"
|
||||
" -n --samples=N Stop sampling at [%d] samples\n"
|
||||
" -x --scale-x=N Scale the graph horizontally [%g] \n"
|
||||
" -y --scale-y=N Scale the graph vertically [%g] \n"
|
||||
" -p --pss Enable PSS graph (CPU intensive)\n"
|
||||
" -e --entropy Enable the entropy_avail graph\n"
|
||||
" -o --output=PATH Path to output files [%s]\n"
|
||||
" -i --init=PATH Path to init executable [%s]\n"
|
||||
" -F --no-filter Disable filtering of unimportant or ephemeral processes\n"
|
||||
" -C --cmdline Display full command lines with arguments\n"
|
||||
" -c --control-group Display process control group\n"
|
||||
" --per-cpu Draw each CPU utilization and wait bar also\n"
|
||||
" -h --help Display this message\n\n"
|
||||
"See bootchart.conf for more information.\n",
|
||||
program_invocation_short_name,
|
||||
DEFAULT_HZ,
|
||||
DEFAULT_SAMPLES_LEN,
|
||||
DEFAULT_SCALE_X,
|
||||
DEFAULT_SCALE_Y,
|
||||
DEFAULT_OUTPUT,
|
||||
DEFAULT_INIT);
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
enum {
|
||||
ARG_PERCPU = 0x100,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{"rel", no_argument, NULL, 'r' },
|
||||
{"freq", required_argument, NULL, 'f' },
|
||||
{"samples", required_argument, NULL, 'n' },
|
||||
{"pss", no_argument, NULL, 'p' },
|
||||
{"output", required_argument, NULL, 'o' },
|
||||
{"init", required_argument, NULL, 'i' },
|
||||
{"no-filter", no_argument, NULL, 'F' },
|
||||
{"cmdline", no_argument, NULL, 'C' },
|
||||
{"control-group", no_argument, NULL, 'c' },
|
||||
{"help", no_argument, NULL, 'h' },
|
||||
{"scale-x", required_argument, NULL, 'x' },
|
||||
{"scale-y", required_argument, NULL, 'y' },
|
||||
{"entropy", no_argument, NULL, 'e' },
|
||||
{"per-cpu", no_argument, NULL, ARG_PERCPU},
|
||||
{}
|
||||
};
|
||||
int c, r;
|
||||
|
||||
if (getpid() == 1)
|
||||
opterr = 0;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "erpf:n:o:i:FCchx:y:", options, NULL)) >= 0)
|
||||
switch (c) {
|
||||
|
||||
case 'r':
|
||||
arg_relative = true;
|
||||
break;
|
||||
case 'f':
|
||||
r = safe_atod(optarg, &arg_hz);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "failed to parse --freq/-f argument '%s': %m",
|
||||
optarg);
|
||||
break;
|
||||
case 'F':
|
||||
arg_filter = false;
|
||||
break;
|
||||
case 'C':
|
||||
arg_show_cmdline = true;
|
||||
break;
|
||||
case 'c':
|
||||
arg_show_cgroup = true;
|
||||
break;
|
||||
case 'n':
|
||||
r = safe_atoi(optarg, &arg_samples_len);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "failed to parse --samples/-n argument '%s': %m",
|
||||
optarg);
|
||||
break;
|
||||
case 'o':
|
||||
path_kill_slashes(optarg);
|
||||
strscpy(arg_output_path, sizeof(arg_output_path), optarg);
|
||||
break;
|
||||
case 'i':
|
||||
path_kill_slashes(optarg);
|
||||
strscpy(arg_init_path, sizeof(arg_init_path), optarg);
|
||||
break;
|
||||
case 'p':
|
||||
arg_pss = true;
|
||||
break;
|
||||
case 'x':
|
||||
r = safe_atod(optarg, &arg_scale_x);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "failed to parse --scale-x/-x argument '%s': %m",
|
||||
optarg);
|
||||
break;
|
||||
case 'y':
|
||||
r = safe_atod(optarg, &arg_scale_y);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "failed to parse --scale-y/-y argument '%s': %m",
|
||||
optarg);
|
||||
break;
|
||||
case 'e':
|
||||
arg_entropy = true;
|
||||
break;
|
||||
case ARG_PERCPU:
|
||||
arg_percpu = true;
|
||||
break;
|
||||
case 'h':
|
||||
help();
|
||||
return 0;
|
||||
case '?':
|
||||
if (getpid() != 1)
|
||||
return -EINVAL;
|
||||
else
|
||||
return 0;
|
||||
default:
|
||||
assert_not_reached("Unhandled option code.");
|
||||
}
|
||||
|
||||
if (arg_hz <= 0) {
|
||||
log_error("Frequency needs to be > 0");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int do_journal_append(char *file) {
|
||||
_cleanup_free_ char *bootchart_message = NULL;
|
||||
_cleanup_free_ char *bootchart_file = NULL;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
struct iovec iovec[5];
|
||||
int r, j = 0;
|
||||
ssize_t n;
|
||||
|
||||
bootchart_file = strappend("BOOTCHART_FILE=", file);
|
||||
if (!bootchart_file)
|
||||
return log_oom();
|
||||
|
||||
IOVEC_SET_STRING(iovec[j++], bootchart_file);
|
||||
IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=9f26aa562cf440c2b16c773d0479b518");
|
||||
IOVEC_SET_STRING(iovec[j++], "PRIORITY=7");
|
||||
bootchart_message = strjoin("MESSAGE=Bootchart created: ", file, NULL);
|
||||
if (!bootchart_message)
|
||||
return log_oom();
|
||||
|
||||
IOVEC_SET_STRING(iovec[j++], bootchart_message);
|
||||
|
||||
p = malloc(10 + BOOTCHART_MAX);
|
||||
if (!p)
|
||||
return log_oom();
|
||||
|
||||
memcpy(p, "BOOTCHART=", 10);
|
||||
|
||||
fd = open(file, O_RDONLY|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return log_error_errno(errno, "Failed to open bootchart data \"%s\": %m", file);
|
||||
|
||||
n = loop_read(fd, p + 10, BOOTCHART_MAX, false);
|
||||
if (n < 0)
|
||||
return log_error_errno(n, "Failed to read bootchart data: %m");
|
||||
|
||||
iovec[j].iov_base = p;
|
||||
iovec[j].iov_len = 10 + n;
|
||||
j++;
|
||||
|
||||
r = sd_journal_sendv(iovec, j);
|
||||
if (r < 0)
|
||||
log_error_errno(r, "Failed to send bootchart: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
static struct list_sample_data *sampledata;
|
||||
_cleanup_closedir_ DIR *proc = NULL;
|
||||
_cleanup_free_ char *build = NULL;
|
||||
_cleanup_fclose_ FILE *of = NULL;
|
||||
_cleanup_close_ int sysfd = -1;
|
||||
struct ps_struct *ps_first;
|
||||
double graph_start;
|
||||
double log_start;
|
||||
double interval;
|
||||
char output_file[PATH_MAX];
|
||||
char datestr[200];
|
||||
int pscount = 0;
|
||||
int n_cpus = 0;
|
||||
int overrun = 0;
|
||||
time_t t = 0;
|
||||
int r, samples;
|
||||
struct ps_struct *ps;
|
||||
struct rlimit rlim;
|
||||
struct list_sample_data *head;
|
||||
struct sigaction sig = {
|
||||
.sa_handler = signal_handler,
|
||||
};
|
||||
|
||||
parse_conf();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
if (r == 0)
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
/*
|
||||
* If the kernel executed us through init=/usr/lib/systemd/systemd-bootchart, then
|
||||
* fork:
|
||||
* - parent execs executable specified via init_path[] (/usr/lib/systemd/systemd by default) as pid=1
|
||||
* - child logs data
|
||||
*/
|
||||
if (getpid() == 1) {
|
||||
if (fork())
|
||||
/* parent */
|
||||
execl(arg_init_path, arg_init_path, NULL);
|
||||
}
|
||||
argv[0][0] = '@';
|
||||
|
||||
rlim.rlim_cur = 4096;
|
||||
rlim.rlim_max = 4096;
|
||||
(void) setrlimit(RLIMIT_NOFILE, &rlim);
|
||||
|
||||
/* start with empty ps LL */
|
||||
ps_first = new0(struct ps_struct, 1);
|
||||
if (!ps_first) {
|
||||
log_oom();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* handle TERM/INT nicely */
|
||||
sigaction(SIGHUP, &sig, NULL);
|
||||
|
||||
interval = (1.0 / arg_hz) * 1000000000.0;
|
||||
|
||||
if (arg_relative)
|
||||
graph_start = log_start = gettime_ns();
|
||||
else {
|
||||
struct timespec n;
|
||||
double uptime;
|
||||
|
||||
clock_gettime(clock_boottime_or_monotonic(), &n);
|
||||
uptime = (n.tv_sec + (n.tv_nsec / (double) NSEC_PER_SEC));
|
||||
|
||||
log_start = gettime_ns();
|
||||
graph_start = log_start - uptime;
|
||||
}
|
||||
|
||||
if (graph_start < 0.0) {
|
||||
log_error("Failed to setup graph start time.\n\n"
|
||||
"The system uptime probably includes time that the system was suspended. "
|
||||
"Use --rel to bypass this issue.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
LIST_HEAD_INIT(head);
|
||||
|
||||
/* main program loop */
|
||||
for (samples = 0; !exiting && samples < arg_samples_len; samples++) {
|
||||
int res;
|
||||
double sample_stop;
|
||||
double elapsed;
|
||||
double timeleft;
|
||||
|
||||
sampledata = new0(struct list_sample_data, 1);
|
||||
if (sampledata == NULL) {
|
||||
log_oom();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
sampledata->sampletime = gettime_ns();
|
||||
sampledata->counter = samples;
|
||||
|
||||
if (sysfd < 0)
|
||||
sysfd = open("/sys", O_RDONLY|O_CLOEXEC);
|
||||
|
||||
if (!build) {
|
||||
if (parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &build, NULL) == -ENOENT)
|
||||
parse_env_file("/usr/lib/os-release", NEWLINE, "PRETTY_NAME", &build, NULL);
|
||||
}
|
||||
|
||||
if (proc)
|
||||
rewinddir(proc);
|
||||
else
|
||||
proc = opendir("/proc");
|
||||
|
||||
/* wait for /proc to become available, discarding samples */
|
||||
if (proc) {
|
||||
r = log_sample(proc, samples, ps_first, &sampledata, &pscount, &n_cpus);
|
||||
if (r < 0)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
sample_stop = gettime_ns();
|
||||
|
||||
elapsed = (sample_stop - sampledata->sampletime) * 1000000000.0;
|
||||
timeleft = interval - elapsed;
|
||||
|
||||
/*
|
||||
* check if we have not consumed our entire timeslice. If we
|
||||
* do, don't sleep and take a new sample right away.
|
||||
* we'll lose all the missed samples and overrun our total
|
||||
* time
|
||||
*/
|
||||
if (timeleft > 0) {
|
||||
struct timespec req;
|
||||
|
||||
req.tv_sec = (time_t)(timeleft / 1000000000.0);
|
||||
req.tv_nsec = (long)(timeleft - (req.tv_sec * 1000000000.0));
|
||||
|
||||
res = nanosleep(&req, NULL);
|
||||
if (res) {
|
||||
if (errno == EINTR)
|
||||
/* caught signal, probably HUP! */
|
||||
break;
|
||||
log_error_errno(errno, "nanosleep() failed: %m");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
} else {
|
||||
overrun++;
|
||||
/* calculate how many samples we lost and scrap them */
|
||||
arg_samples_len -= (int)(-timeleft / interval);
|
||||
}
|
||||
LIST_PREPEND(link, head, sampledata);
|
||||
}
|
||||
|
||||
/* do some cleanup, close fd's */
|
||||
ps = ps_first;
|
||||
while (ps->next_ps) {
|
||||
ps = ps->next_ps;
|
||||
ps->schedstat = safe_close(ps->schedstat);
|
||||
ps->sched = safe_close(ps->sched);
|
||||
ps->smaps = safe_fclose(ps->smaps);
|
||||
}
|
||||
|
||||
if (!of) {
|
||||
t = time(NULL);
|
||||
r = strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
|
||||
assert_se(r > 0);
|
||||
|
||||
snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", arg_output_path, datestr);
|
||||
of = fopen(output_file, "we");
|
||||
}
|
||||
|
||||
if (!of) {
|
||||
log_error("Error opening output file '%s': %m\n", output_file);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
r = svg_do(of, strna(build), head, ps_first,
|
||||
samples, pscount, n_cpus, graph_start,
|
||||
log_start, interval, overrun);
|
||||
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Error generating svg file: %m");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
log_info("systemd-bootchart wrote %s\n", output_file);
|
||||
|
||||
r = do_journal_append(output_file);
|
||||
if (r < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
/* nitpic cleanups */
|
||||
ps = ps_first->next_ps;
|
||||
while (ps->next_ps) {
|
||||
struct ps_struct *old;
|
||||
|
||||
old = ps;
|
||||
old->sample = ps->first;
|
||||
ps = ps->next_ps;
|
||||
while (old->sample->next) {
|
||||
struct ps_sched_struct *oldsample = old->sample;
|
||||
|
||||
old->sample = old->sample->next;
|
||||
free(oldsample);
|
||||
}
|
||||
free(old->cgroup);
|
||||
free(old->sample);
|
||||
free(old);
|
||||
}
|
||||
|
||||
free(ps->cgroup);
|
||||
free(ps->sample);
|
||||
free(ps);
|
||||
|
||||
sampledata = head;
|
||||
while (sampledata->link_prev) {
|
||||
struct list_sample_data *old_sampledata = sampledata;
|
||||
sampledata = sampledata->link_prev;
|
||||
free(old_sampledata);
|
||||
}
|
||||
free(sampledata);
|
||||
|
||||
/* don't complain when overrun once, happens most commonly on 1st sample */
|
||||
if (overrun > 1)
|
||||
log_warning("systemd-bootchart: sample time overrun %i times\n", overrun);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Entries in this file show the compile time defaults.
|
||||
# You can change settings by editing this file.
|
||||
# Defaults can be restored by simply deleting this file.
|
||||
#
|
||||
# See bootchart.conf(5) for details.
|
||||
|
||||
[Bootchart]
|
||||
#Samples=500
|
||||
#Frequency=25
|
||||
#Relative=no
|
||||
#Filter=yes
|
||||
#Output=<folder name, defaults to /run/log>
|
||||
#Init=/path/to/init-binary
|
||||
#PlotMemoryUsage=no
|
||||
#PlotEntropyGraph=no
|
||||
#ScaleX=100
|
||||
#ScaleY=20
|
||||
#ControlGroup=no
|
||||
#PerCPU=no
|
@ -1,119 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright (C) 2009-2013 Intel Corporation
|
||||
|
||||
Authors:
|
||||
Auke Kok <auke-jan.h.kok@intel.com>
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "list.h"
|
||||
|
||||
#define MAXCPUS 16
|
||||
#define MAXPIDS 65535
|
||||
|
||||
struct block_stat_struct {
|
||||
/* /proc/vmstat pgpgin & pgpgout */
|
||||
int bi;
|
||||
int bo;
|
||||
};
|
||||
|
||||
struct cpu_stat_sample_struct {
|
||||
/* /proc/schedstat fields 10 & 11 (after name) */
|
||||
double runtime;
|
||||
double waittime;
|
||||
};
|
||||
|
||||
/* per process, per sample data we will log */
|
||||
struct ps_sched_struct {
|
||||
/* /proc/<n>/schedstat fields 1 & 2 */
|
||||
double runtime;
|
||||
double waittime;
|
||||
int pss;
|
||||
struct list_sample_data *sampledata;
|
||||
struct ps_sched_struct *next;
|
||||
struct ps_sched_struct *prev;
|
||||
struct ps_sched_struct *cross; /* cross pointer */
|
||||
struct ps_struct *ps_new;
|
||||
};
|
||||
|
||||
struct list_sample_data {
|
||||
double runtime[MAXCPUS];
|
||||
double waittime[MAXCPUS];
|
||||
double sampletime;
|
||||
int entropy_avail;
|
||||
struct block_stat_struct blockstat;
|
||||
LIST_FIELDS(struct list_sample_data, link); /* DLL */
|
||||
int counter;
|
||||
};
|
||||
|
||||
/* process info */
|
||||
struct ps_struct {
|
||||
struct ps_struct *next_ps; /* SLL pointer */
|
||||
struct ps_struct *parent; /* ppid ref */
|
||||
struct ps_struct *children; /* children */
|
||||
struct ps_struct *next; /* siblings */
|
||||
|
||||
/* must match - otherwise it's a new process with same PID */
|
||||
char name[256];
|
||||
int pid;
|
||||
int ppid;
|
||||
char *cgroup;
|
||||
|
||||
/* cache fd's */
|
||||
int sched;
|
||||
int schedstat;
|
||||
FILE *smaps;
|
||||
|
||||
/* pointers to first/last seen timestamps */
|
||||
struct ps_sched_struct *first;
|
||||
struct ps_sched_struct *last;
|
||||
|
||||
/* records actual start time, may be way before bootchart runs */
|
||||
double starttime;
|
||||
|
||||
/* record human readable total cpu time */
|
||||
double total;
|
||||
|
||||
/* largest PSS size found */
|
||||
int pss_max;
|
||||
|
||||
/* for drawing connection lines later */
|
||||
double pos_x;
|
||||
double pos_y;
|
||||
|
||||
struct ps_sched_struct *sample;
|
||||
};
|
||||
|
||||
extern bool arg_relative;
|
||||
extern bool arg_filter;
|
||||
extern bool arg_show_cmdline;
|
||||
extern bool arg_show_cgroup;
|
||||
extern bool arg_pss;
|
||||
extern bool arg_entropy;
|
||||
extern bool arg_percpu;
|
||||
extern bool arg_initcall;
|
||||
extern int arg_samples_len;
|
||||
extern double arg_hz;
|
||||
extern double arg_scale_x;
|
||||
extern double arg_scale_y;
|
||||
|
||||
extern char arg_output_path[PATH_MAX];
|
||||
extern char arg_init_path[PATH_MAX];
|
@ -1,555 +0,0 @@
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright (C) 2009-2013 Intel Corporation
|
||||
|
||||
Authors:
|
||||
Auke Kok <auke-jan.h.kok@intel.com>
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bootchart.h"
|
||||
#include "cgroup-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "parse-util.h"
|
||||
#include "store.h"
|
||||
#include "string-util.h"
|
||||
#include "strxcpyx.h"
|
||||
#include "time-util.h"
|
||||
#include "util.h"
|
||||
|
||||
/*
|
||||
* Alloc a static 4k buffer for stdio - primarily used to increase
|
||||
* PSS buffering from the default 1k stdin buffer to reduce
|
||||
* read() overhead.
|
||||
*/
|
||||
static char smaps_buf[4096];
|
||||
static int skip = 0;
|
||||
|
||||
double gettime_ns(void) {
|
||||
struct timespec n;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &n);
|
||||
|
||||
return (n.tv_sec + (n.tv_nsec / (double) NSEC_PER_SEC));
|
||||
}
|
||||
|
||||
static char *bufgetline(char *buf) {
|
||||
char *c;
|
||||
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
c = strchr(buf, '\n');
|
||||
if (c)
|
||||
c++;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static int pid_cmdline_strscpy(int procfd, char *buffer, size_t buf_len, int pid) {
|
||||
char filename[PATH_MAX];
|
||||
_cleanup_close_ int fd = -1;
|
||||
ssize_t n;
|
||||
|
||||
sprintf(filename, "%d/cmdline", pid);
|
||||
fd = openat(procfd, filename, O_RDONLY|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
n = read(fd, buffer, buf_len-1);
|
||||
if (n > 0) {
|
||||
int i;
|
||||
for (i = 0; i < n; i++)
|
||||
if (buffer[i] == '\0')
|
||||
buffer[i] = ' ';
|
||||
buffer[n] = '\0';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int log_sample(DIR *proc,
|
||||
int sample,
|
||||
struct ps_struct *ps_first,
|
||||
struct list_sample_data **ptr,
|
||||
int *pscount,
|
||||
int *cpus) {
|
||||
|
||||
static int vmstat = -1;
|
||||
_cleanup_free_ char *buf_schedstat = NULL;
|
||||
char buf[4096];
|
||||
char key[256];
|
||||
char val[256];
|
||||
char rt[256];
|
||||
char wt[256];
|
||||
char *m;
|
||||
int r;
|
||||
int c;
|
||||
int p;
|
||||
int mod;
|
||||
static int e_fd = -1;
|
||||
ssize_t s;
|
||||
ssize_t n;
|
||||
struct dirent *ent;
|
||||
int fd;
|
||||
struct list_sample_data *sampledata;
|
||||
struct ps_sched_struct *ps_prev = NULL;
|
||||
int procfd;
|
||||
int taskfd = -1;
|
||||
|
||||
sampledata = *ptr;
|
||||
|
||||
procfd = dirfd(proc);
|
||||
if (procfd < 0)
|
||||
return -errno;
|
||||
|
||||
if (vmstat < 0) {
|
||||
/* block stuff */
|
||||
vmstat = openat(procfd, "vmstat", O_RDONLY|O_CLOEXEC);
|
||||
if (vmstat < 0)
|
||||
return log_error_errno(errno, "Failed to open /proc/vmstat: %m");
|
||||
}
|
||||
|
||||
n = pread(vmstat, buf, sizeof(buf) - 1, 0);
|
||||
if (n <= 0) {
|
||||
vmstat = safe_close(vmstat);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
buf[n] = '\0';
|
||||
|
||||
m = buf;
|
||||
while (m) {
|
||||
if (sscanf(m, "%s %s", key, val) < 2)
|
||||
goto vmstat_next;
|
||||
if (streq(key, "pgpgin"))
|
||||
sampledata->blockstat.bi = atoi(val);
|
||||
if (streq(key, "pgpgout")) {
|
||||
sampledata->blockstat.bo = atoi(val);
|
||||
break;
|
||||
}
|
||||
vmstat_next:
|
||||
m = bufgetline(m);
|
||||
if (!m)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Parse "/proc/schedstat" for overall CPU utilization */
|
||||
r = read_full_file("/proc/schedstat", &buf_schedstat, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Unable to read schedstat: %m");
|
||||
|
||||
m = buf_schedstat;
|
||||
while (m) {
|
||||
if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3)
|
||||
goto schedstat_next;
|
||||
|
||||
if (strstr(key, "cpu")) {
|
||||
r = safe_atoi((const char*)(key+3), &c);
|
||||
if (r < 0 || c > MAXCPUS -1)
|
||||
/* Oops, we only have room for MAXCPUS data */
|
||||
break;
|
||||
sampledata->runtime[c] = atoll(rt);
|
||||
sampledata->waittime[c] = atoll(wt);
|
||||
|
||||
if (c == *cpus)
|
||||
*cpus = c + 1;
|
||||
}
|
||||
schedstat_next:
|
||||
m = bufgetline(m);
|
||||
if (!m)
|
||||
break;
|
||||
}
|
||||
|
||||
if (arg_entropy) {
|
||||
if (e_fd < 0) {
|
||||
e_fd = openat(procfd, "sys/kernel/random/entropy_avail", O_RDONLY|O_CLOEXEC);
|
||||
if (e_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open /proc/sys/kernel/random/entropy_avail: %m");
|
||||
}
|
||||
|
||||
n = pread(e_fd, buf, sizeof(buf) - 1, 0);
|
||||
if (n <= 0) {
|
||||
e_fd = safe_close(e_fd);
|
||||
} else {
|
||||
buf[n] = '\0';
|
||||
sampledata->entropy_avail = atoi(buf);
|
||||
}
|
||||
}
|
||||
|
||||
while ((ent = readdir(proc)) != NULL) {
|
||||
char filename[PATH_MAX];
|
||||
int pid;
|
||||
struct ps_struct *ps;
|
||||
|
||||
if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
|
||||
continue;
|
||||
|
||||
pid = atoi(ent->d_name);
|
||||
|
||||
if (pid >= MAXPIDS)
|
||||
continue;
|
||||
|
||||
ps = ps_first;
|
||||
while (ps->next_ps) {
|
||||
ps = ps->next_ps;
|
||||
if (ps->pid == pid)
|
||||
break;
|
||||
}
|
||||
|
||||
/* end of our LL? then append a new record */
|
||||
if (ps->pid != pid) {
|
||||
_cleanup_fclose_ FILE *st = NULL;
|
||||
char t[32];
|
||||
struct ps_struct *parent;
|
||||
|
||||
ps->next_ps = new0(struct ps_struct, 1);
|
||||
if (!ps->next_ps)
|
||||
return log_oom();
|
||||
|
||||
ps = ps->next_ps;
|
||||
ps->pid = pid;
|
||||
ps->sched = -1;
|
||||
ps->schedstat = -1;
|
||||
|
||||
ps->sample = new0(struct ps_sched_struct, 1);
|
||||
if (!ps->sample)
|
||||
return log_oom();
|
||||
|
||||
ps->sample->sampledata = sampledata;
|
||||
|
||||
(*pscount)++;
|
||||
|
||||
/* mark our first sample */
|
||||
ps->first = ps->last = ps->sample;
|
||||
ps->sample->runtime = atoll(rt);
|
||||
ps->sample->waittime = atoll(wt);
|
||||
|
||||
/* get name, start time */
|
||||
if (ps->sched < 0) {
|
||||
sprintf(filename, "%d/sched", pid);
|
||||
ps->sched = openat(procfd, filename, O_RDONLY|O_CLOEXEC);
|
||||
if (ps->sched < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
|
||||
if (s <= 0) {
|
||||
ps->sched = safe_close(ps->sched);
|
||||
continue;
|
||||
}
|
||||
buf[s] = '\0';
|
||||
|
||||
if (!sscanf(buf, "%s %*s %*s", key))
|
||||
continue;
|
||||
|
||||
strscpy(ps->name, sizeof(ps->name), key);
|
||||
|
||||
/* cmdline */
|
||||
if (arg_show_cmdline)
|
||||
pid_cmdline_strscpy(procfd, ps->name, sizeof(ps->name), pid);
|
||||
|
||||
/* discard line 2 */
|
||||
m = bufgetline(buf);
|
||||
if (!m)
|
||||
continue;
|
||||
|
||||
m = bufgetline(m);
|
||||
if (!m)
|
||||
continue;
|
||||
|
||||
if (!sscanf(m, "%*s %*s %s", t))
|
||||
continue;
|
||||
|
||||
r = safe_atod(t, &ps->starttime);
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
ps->starttime /= 1000.0;
|
||||
|
||||
if (arg_show_cgroup)
|
||||
/* if this fails, that's OK */
|
||||
cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER,
|
||||
ps->pid, &ps->cgroup);
|
||||
|
||||
/* ppid */
|
||||
sprintf(filename, "%d/stat", pid);
|
||||
fd = openat(procfd, filename, O_RDONLY|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
continue;
|
||||
|
||||
st = fdopen(fd, "re");
|
||||
if (!st) {
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!fscanf(st, "%*s %*s %*s %i", &p))
|
||||
continue;
|
||||
|
||||
ps->ppid = p;
|
||||
|
||||
/*
|
||||
* setup child pointers
|
||||
*
|
||||
* these are used to paint the tree coherently later
|
||||
* each parent has a LL of children, and a LL of siblings
|
||||
*/
|
||||
if (pid == 1)
|
||||
continue; /* nothing to do for init atm */
|
||||
|
||||
/* kthreadd has ppid=0, which breaks our tree ordering */
|
||||
if (ps->ppid == 0)
|
||||
ps->ppid = 1;
|
||||
|
||||
parent = ps_first;
|
||||
while ((parent->next_ps && parent->pid != ps->ppid))
|
||||
parent = parent->next_ps;
|
||||
|
||||
if (parent->pid != ps->ppid) {
|
||||
/* orphan */
|
||||
ps->ppid = 1;
|
||||
parent = ps_first->next_ps;
|
||||
}
|
||||
|
||||
ps->parent = parent;
|
||||
|
||||
if (!parent->children) {
|
||||
/* it's the first child */
|
||||
parent->children = ps;
|
||||
} else {
|
||||
/* walk all children and append */
|
||||
struct ps_struct *children;
|
||||
children = parent->children;
|
||||
while (children->next)
|
||||
children = children->next;
|
||||
|
||||
children->next = ps;
|
||||
}
|
||||
}
|
||||
|
||||
/* else -> found pid, append data in ps */
|
||||
|
||||
/* below here is all continuous logging parts - we get here on every
|
||||
* iteration */
|
||||
|
||||
/* rt, wt */
|
||||
if (ps->schedstat < 0) {
|
||||
sprintf(filename, "%d/schedstat", pid);
|
||||
ps->schedstat = openat(procfd, filename, O_RDONLY|O_CLOEXEC);
|
||||
if (ps->schedstat < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
s = pread(ps->schedstat, buf, sizeof(buf) - 1, 0);
|
||||
if (s <= 0) {
|
||||
/* clean up our file descriptors - assume that the process exited */
|
||||
close(ps->schedstat);
|
||||
ps->schedstat = -1;
|
||||
ps->sched = safe_close(ps->sched);
|
||||
continue;
|
||||
}
|
||||
|
||||
buf[s] = '\0';
|
||||
|
||||
if (!sscanf(buf, "%s %s %*s", rt, wt))
|
||||
continue;
|
||||
|
||||
ps->sample->next = new0(struct ps_sched_struct, 1);
|
||||
if (!ps->sample->next)
|
||||
return log_oom();
|
||||
|
||||
ps->sample->next->prev = ps->sample;
|
||||
ps->sample = ps->sample->next;
|
||||
ps->last = ps->sample;
|
||||
ps->sample->runtime = atoll(rt);
|
||||
ps->sample->waittime = atoll(wt);
|
||||
ps->sample->sampledata = sampledata;
|
||||
ps->sample->ps_new = ps;
|
||||
if (ps_prev)
|
||||
ps_prev->cross = ps->sample;
|
||||
|
||||
ps_prev = ps->sample;
|
||||
ps->total = (ps->last->runtime - ps->first->runtime)
|
||||
/ 1000000000.0;
|
||||
|
||||
/* Take into account CPU runtime/waittime spent in non-main threads of the process
|
||||
* by parsing "/proc/[pid]/task/[tid]/schedstat" for all [tid] != [pid]
|
||||
* See https://github.com/systemd/systemd/issues/139
|
||||
*/
|
||||
|
||||
/* Browse directory "/proc/[pid]/task" to know the thread ids of process [pid] */
|
||||
snprintf(filename, sizeof(filename), PID_FMT "/task", pid);
|
||||
taskfd = openat(procfd, filename, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
if (taskfd >= 0) {
|
||||
_cleanup_closedir_ DIR *taskdir = NULL;
|
||||
|
||||
taskdir = fdopendir(taskfd);
|
||||
if (!taskdir) {
|
||||
safe_close(taskfd);
|
||||
return -errno;
|
||||
}
|
||||
FOREACH_DIRENT(ent, taskdir, break) {
|
||||
int tid = -1;
|
||||
_cleanup_close_ int tid_schedstat = -1;
|
||||
long long delta_rt;
|
||||
long long delta_wt;
|
||||
|
||||
if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
|
||||
continue;
|
||||
|
||||
/* Skip main thread as it was already accounted */
|
||||
r = safe_atoi(ent->d_name, &tid);
|
||||
if (r < 0 || tid == pid)
|
||||
continue;
|
||||
|
||||
/* Parse "/proc/[pid]/task/[tid]/schedstat" */
|
||||
snprintf(filename, sizeof(filename), PID_FMT "/schedstat", tid);
|
||||
tid_schedstat = openat(taskfd, filename, O_RDONLY|O_CLOEXEC);
|
||||
|
||||
if (tid_schedstat == -1)
|
||||
continue;
|
||||
|
||||
s = pread(tid_schedstat, buf, sizeof(buf) - 1, 0);
|
||||
if (s <= 0)
|
||||
continue;
|
||||
buf[s] = '\0';
|
||||
|
||||
if (!sscanf(buf, "%s %s %*s", rt, wt))
|
||||
continue;
|
||||
|
||||
r = safe_atolli(rt, &delta_rt);
|
||||
if (r < 0)
|
||||
continue;
|
||||
r = safe_atolli(rt, &delta_wt);
|
||||
if (r < 0)
|
||||
continue;
|
||||
ps->sample->runtime += delta_rt;
|
||||
ps->sample->waittime += delta_wt;
|
||||
}
|
||||
}
|
||||
|
||||
if (!arg_pss)
|
||||
goto catch_rename;
|
||||
|
||||
/* Pss */
|
||||
if (!ps->smaps) {
|
||||
sprintf(filename, "%d/smaps", pid);
|
||||
fd = openat(procfd, filename, O_RDONLY|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
continue;
|
||||
ps->smaps = fdopen(fd, "re");
|
||||
if (!ps->smaps) {
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
|
||||
} else {
|
||||
rewind(ps->smaps);
|
||||
}
|
||||
|
||||
/* test to see if we need to skip another field */
|
||||
if (skip == 0) {
|
||||
if (fgets(buf, sizeof(buf), ps->smaps) == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (fread(buf, 1, 28 * 15, ps->smaps) != (28 * 15)) {
|
||||
continue;
|
||||
}
|
||||
if (buf[392] == 'V') {
|
||||
skip = 2;
|
||||
}
|
||||
else {
|
||||
skip = 1;
|
||||
}
|
||||
rewind(ps->smaps);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int pss_kb;
|
||||
|
||||
/* skip one line, this contains the object mapped. */
|
||||
if (fgets(buf, sizeof(buf), ps->smaps) == NULL) {
|
||||
break;
|
||||
}
|
||||
/* then there's a 28 char 14 line block */
|
||||
if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14) {
|
||||
break;
|
||||
}
|
||||
pss_kb = atoi(&buf[61]);
|
||||
ps->sample->pss += pss_kb;
|
||||
|
||||
/* skip one more line if this is a newer kernel */
|
||||
if (skip == 2) {
|
||||
if (fgets(buf, sizeof(buf), ps->smaps) == NULL)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ps->sample->pss > ps->pss_max)
|
||||
ps->pss_max = ps->sample->pss;
|
||||
|
||||
catch_rename:
|
||||
/* catch process rename, try to randomize time */
|
||||
mod = (arg_hz < 4.0) ? 4.0 : (arg_hz / 4.0);
|
||||
if (((sample - ps->pid) + pid) % (int)(mod) == 0) {
|
||||
|
||||
/* re-fetch name */
|
||||
/* get name, start time */
|
||||
if (ps->sched < 0) {
|
||||
sprintf(filename, "%d/sched", pid);
|
||||
ps->sched = openat(procfd, filename, O_RDONLY|O_CLOEXEC);
|
||||
if (ps->sched < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
|
||||
if (s <= 0) {
|
||||
/* clean up file descriptors */
|
||||
ps->sched = safe_close(ps->sched);
|
||||
ps->schedstat = safe_close(ps->schedstat);
|
||||
continue;
|
||||
}
|
||||
|
||||
buf[s] = '\0';
|
||||
|
||||
if (!sscanf(buf, "%s %*s %*s", key))
|
||||
continue;
|
||||
|
||||
strscpy(ps->name, sizeof(ps->name), key);
|
||||
|
||||
/* cmdline */
|
||||
if (arg_show_cmdline)
|
||||
pid_cmdline_strscpy(procfd, ps->name, sizeof(ps->name), pid);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright (C) 2009-2013 Intel Corporation
|
||||
|
||||
Authors:
|
||||
Auke Kok <auke-jan.h.kok@intel.com>
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include "bootchart.h"
|
||||
|
||||
double gettime_ns(void);
|
||||
void log_uptime(void);
|
||||
int log_sample(DIR *proc,
|
||||
int sample,
|
||||
struct ps_struct *ps_first,
|
||||
struct list_sample_data **ptr,
|
||||
int *pscount,
|
||||
int *cpus);
|
1375
src/bootchart/svg.c
1375
src/bootchart/svg.c
File diff suppressed because it is too large
Load Diff
@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright (C) 2009-2013 Intel Corporation
|
||||
|
||||
Authors:
|
||||
Auke Kok <auke-jan.h.kok@intel.com>
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
int svg_do(FILE *of,
|
||||
const char *build,
|
||||
struct list_sample_data *head,
|
||||
struct ps_struct *ps_first,
|
||||
int n_samples,
|
||||
int pscount,
|
||||
int n_cpus,
|
||||
double graph_start,
|
||||
double log_start,
|
||||
double interval,
|
||||
int overrun);
|
@ -82,8 +82,6 @@ _SD_BEGIN_DECLARATIONS;
|
||||
|
||||
#define SD_MESSAGE_INVALID_CONFIGURATION SD_ID128_MAKE(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01)
|
||||
|
||||
#define SD_MESSAGE_BOOTCHART SD_ID128_MAKE(9f,26,aa,56,2c,f4,40,c2,b1,6c,77,3d,04,79,b5,18)
|
||||
|
||||
#define SD_MESSAGE_DNSSEC_FAILURE SD_ID128_MAKE(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d)
|
||||
#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED SD_ID128_MAKE(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65)
|
||||
#define SD_MESSAGE_DNSSEC_DOWNGRADE SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57)
|
||||
|
@ -131,15 +131,6 @@ TEMPLATE = '''\
|
||||
<variablelist id='systemd-directives' />
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>bootchart.conf directives</title>
|
||||
|
||||
<para>Directives for configuring the behaviour of the
|
||||
systemd-bootchart process.</para>
|
||||
|
||||
<variablelist id='bootchart-directives' />
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>command line options</title>
|
||||
|
||||
|
1
units/.gitignore
vendored
1
units/.gitignore
vendored
@ -22,7 +22,6 @@
|
||||
/systemd-ask-password-wall.service
|
||||
/systemd-backlight@.service
|
||||
/systemd-binfmt.service
|
||||
/systemd-bootchart.service
|
||||
/systemd-coredump@.service
|
||||
/systemd-firstboot.service
|
||||
/systemd-fsck-root.service
|
||||
|
@ -1,20 +0,0 @@
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# Note: it's usually a better idea to run systemd-bootchart via the
|
||||
# init= kernel command line switch. See the man page for details.
|
||||
|
||||
[Unit]
|
||||
Description=Boot Process Profiler
|
||||
Documentation=man:systemd-bootchart.service(1) man:bootchart.conf(5)
|
||||
DefaultDependencies=no
|
||||
|
||||
[Service]
|
||||
ExecStart=@rootlibexecdir@/systemd-bootchart -r
|
||||
|
||||
[Install]
|
||||
WantedBy=sysinit.target
|
Loading…
Reference in New Issue
Block a user