<div dir="ltr">Hi,<div><br></div><div style>I have resubmitted the patch with agreed changes. </div><div style><br></div><div style>My usecase is pretty simple actually. I would like to store process time of services right after default.target and store the values in a database. That involves running cgtop 1 time and parsing the output. </div>

<div style><br></div><div style>Thanks.</div></div><div class="gmail_extra"><br><br><div class="gmail_quote">On Mon, Apr 1, 2013 at 2:23 PM, Zbigniew Jędrzejewski-Szmek <span dir="ltr"><<a href="mailto:zbyszek@in.waw.pl" target="_blank">zbyszek@in.waw.pl</a>></span> wrote:<br>

<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="HOEnZb"><div class="h5">On Mon, Apr 01, 2013 at 10:01:40AM +0200, Umut Tezduyar wrote:<br>
> Hi Zbigniew,<br>
><br>
> Thank you very much for reviewing. Please find my comments below.<br>
><br>
><br>
> On Mon, Apr 1, 2013 at 6:19 AM, Zbigniew Jędrzejewski-Szmek <<br>
> <a href="mailto:zbyszek@in.waw.pl">zbyszek@in.waw.pl</a>> wrote:<br>
><br>
> > On Sun, Mar 31, 2013 at 08:22:05PM +0200, Umut Tezduyar wrote:<br>
> > > ---<br>
> > >  src/cgtop/cgtop.c |  108<br>
> > ++++++++++++++++++++++++++++++++++++-----------------<br>
> > >  1 files changed, 74 insertions(+), 34 deletions(-)<br>
> > ><br>
> > > diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c<br>
> > > index eebebf0..0ef4fcc 100644<br>
> > > --- a/src/cgtop/cgtop.c<br>
> > > +++ b/src/cgtop/cgtop.c<br>
> > > @@ -69,6 +69,11 @@ static enum {<br>
> > >          ORDER_IO<br>
> > >  } arg_order = ORDER_CPU;<br>
> > ><br>
> > > +static enum {<br>
> > > +        CPU_PERCENT,<br>
> > > +        CPU_TIME,<br>
> > > +} arg_cpu_type = CPU_PERCENT;<br>
> > > +<br>
> > >  static void group_free(Group *g) {<br>
> > >          assert(g);<br>
> > ><br>
> > > @@ -372,16 +377,22 @@ static int group_compare(const void*a, const void<br>
> > *b) {<br>
> > >                  return 1;<br>
> > ><br>
> > >          if (arg_order == ORDER_CPU) {<br>
> > > -                if (x->cpu_valid && y->cpu_valid) {<br>
> > > -<br>
> > > -                        if (x->cpu_fraction > y->cpu_fraction)<br>
> > > +                if (arg_cpu_type == CPU_PERCENT) {<br>
> > > +                        if (x->cpu_valid && y->cpu_valid) {<br>
> > > +                                if (x->cpu_fraction > y->cpu_fraction)<br>
> > > +                                        return -1;<br>
> > > +                                else if (x->cpu_fraction <<br>
> > y->cpu_fraction)<br>
> > > +                                        return 1;<br>
> > > +                        } else if (x->cpu_valid)<br>
> > >                                  return -1;<br>
> > > -                        else if (x->cpu_fraction < y->cpu_fraction)<br>
> > > +                        else if (y->cpu_valid)<br>
> > >                                  return 1;<br>
> > > -                } else if (x->cpu_valid)<br>
> > > -                        return -1;<br>
> > > -                else if (y->cpu_valid)<br>
> > > -                        return 1;<br>
> > > +                } else {<br>
> > > +                        if (x->cpu_usage > y->cpu_usage)<br>
> > > +                                return -1;<br>
> > > +                        else if (x->cpu_usage < y->cpu_usage)<br>
> > > +                                return 1;<br>
> > > +                }<br>
> > >          }<br>
> > ><br>
> > >          if (arg_order == ORDER_TASKS) {<br>
> > > @@ -428,13 +439,16 @@ static int display(Hashmap *a) {<br>
> > >          Iterator i;<br>
> > >          Group *g;<br>
> > >          Group **array;<br>
> > > -        unsigned rows, path_columns, n = 0, j;<br>
> > > +        signed path_columns;<br>
> > > +        unsigned rows, n = 0, j, maxtcpu = 0, maxtpath = 0;<br>
> > > +        char cpu_title[21];<br>
> > ><br>
> > >          assert(a);<br>
> > ><br>
> > >          /* Set cursor to top left corner and clear screen */<br>
> > > -        fputs("\033[H"<br>
> > > -              "\033[2J", stdout);<br>
> > > +        if (on_tty())<br>
> > > +                fputs("\033[H"<br>
> > > +                      "\033[2J", stdout);<br>
> > ><br>
> > >          array = alloca(sizeof(Group*) * hashmap_size(a));<br>
> > ><br>
> > > @@ -444,33 +458,50 @@ static int display(Hashmap *a) {<br>
> > ><br>
> > >          qsort(array, n, sizeof(Group*), group_compare);<br>
> > ><br>
> > > +        /* Find longest names in one run */<br>
> > > +        for (j = 0; j < n; j++) {<br>
> > > +                snprintf(cpu_title, sizeof(cpu_title), "%llu",<br>
> > array[j]->cpu_usage);<br>
> > > +                if (strlen(cpu_title) > maxtcpu)<br>
> > > +                        maxtcpu = strlen(cpu_title);<br>
> > > +                if (strlen(array[j]->path) > maxtpath)<br>
> > > +                        maxtpath = strlen(array[j]->path);<br>
> > The compiler *might* be smart enough, but let's not depend on that.<br>
> > strlen() result should be cached. Maybe just use MAX?<br>
> ><br>
> Umut: Sounds reasonable.<br>
><br>
> ><br>
> > > +        }<br>
> > > +<br>
> > > +        if (arg_cpu_type == CPU_PERCENT)<br>
> > > +                snprintf(cpu_title, sizeof(cpu_title), "%6s", "%CPU");<br>
> > > +        else<br>
> > > +                snprintf(cpu_title, sizeof(cpu_title), "%*s", maxtcpu,<br>
> > "CPU Time");<br>
> > > +<br>
> > >          rows = lines();<br>
> > >          if (rows <= 10)<br>
> > >                  rows = 10;<br>
> > ><br>
> > > -        path_columns = columns() - 42;<br>
> > > -        if (path_columns < 10)<br>
> > > -                path_columns = 10;<br>
> > > -<br>
> > > -        printf("%s%-*s%s %s%7s%s %s%6s%s %s%8s%s %s%8s%s %s%8s%s\n\n",<br>
> > > -               arg_order == ORDER_PATH   ? ANSI_HIGHLIGHT_ON : "",<br>
> > path_columns, "Path",<br>
> > > -                      arg_order == ORDER_PATH   ? ANSI_HIGHLIGHT_OFF :<br>
> > "",<br>
> > > -               arg_order == ORDER_TASKS  ? ANSI_HIGHLIGHT_ON : "",<br>
> > "Tasks",<br>
> > > -                      arg_order == ORDER_TASKS  ? ANSI_HIGHLIGHT_OFF :<br>
> > "",<br>
> > > -               arg_order == ORDER_CPU    ? ANSI_HIGHLIGHT_ON : "",<br>
> > "%CPU",<br>
> > > -                      arg_order == ORDER_CPU    ? ANSI_HIGHLIGHT_OFF :<br>
> > "",<br>
> > > -               arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_ON : "",<br>
> > "Memory",<br>
> > > -                      arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_OFF :<br>
> > "",<br>
> > > -               arg_order == ORDER_IO     ? ANSI_HIGHLIGHT_ON : "",<br>
> > "Input/s",<br>
> > > -                      arg_order == ORDER_IO     ? ANSI_HIGHLIGHT_OFF :<br>
> > "",<br>
> > > -               arg_order == ORDER_IO     ? ANSI_HIGHLIGHT_ON : "",<br>
> > "Output/s",<br>
> > > -                      arg_order == ORDER_IO     ? ANSI_HIGHLIGHT_OFF :<br>
> > "");<br>
> > > +        if (on_tty()) {<br>
> > > +                path_columns = columns() - 36 - strlen(cpu_title);<br>
> > > +                if (path_columns < 10)<br>
> > > +                        path_columns = 10;<br>
> > > +        } else<br>
> > > +                path_columns = maxtpath;<br>
> > > +<br>
> > > +        printf("%s%-*s%s %s%7s%s %s%s%s %s%8s%s %s%8s%s %s%8s%s\n\n",<br>
> > > +               on_tty() && arg_order == ORDER_PATH   ?<br>
> > ANSI_HIGHLIGHT_ON : "", path_columns, "Path",<br>
> > > +                      on_tty() && arg_order == ORDER_PATH   ?<br>
> > ANSI_HIGHLIGHT_OFF : "",<br>
> > > +               on_tty() && arg_order == ORDER_TASKS  ?<br>
> > ANSI_HIGHLIGHT_ON : "", "Tasks",<br>
> > > +                      on_tty() && arg_order == ORDER_TASKS  ?<br>
> > ANSI_HIGHLIGHT_OFF : "",<br>
> > > +               on_tty() && arg_order == ORDER_CPU    ?<br>
> > ANSI_HIGHLIGHT_ON : "", cpu_title,<br>
> > > +                      on_tty() && arg_order == ORDER_CPU    ?<br>
> > ANSI_HIGHLIGHT_OFF : "",<br>
> > > +               on_tty() && arg_order == ORDER_MEMORY ?<br>
> > ANSI_HIGHLIGHT_ON : "", "Memory",<br>
> > > +                      on_tty() && arg_order == ORDER_MEMORY ?<br>
> > ANSI_HIGHLIGHT_OFF : "",<br>
> > > +               on_tty() && arg_order == ORDER_IO     ?<br>
> > ANSI_HIGHLIGHT_ON : "", "Input/s",<br>
> > > +                      on_tty() && arg_order == ORDER_IO     ?<br>
> > ANSI_HIGHLIGHT_OFF : "",<br>
> > > +               on_tty() && arg_order == ORDER_IO     ?<br>
> > ANSI_HIGHLIGHT_ON : "", "Output/s",<br>
> > > +                      on_tty() && arg_order == ORDER_IO     ?<br>
> > ANSI_HIGHLIGHT_OFF : "");<br>
> > What about defining<br>
> >   const char *on = on_tty() ? ANSI_HIGHLIGHT_ON : "", *off = on_tty() ?<br>
> > ANSI_HIGHLIGHT_OFF : "",<br>
> > to make this easier to read?<br>
> ><br>
> Umut: I agree.<br>
><br>
> ><br>
> > ><br>
> > >          for (j = 0; j < n; j++) {<br>
> > >                  char *p;<br>
> > >                  char m[FORMAT_BYTES_MAX];<br>
> > ><br>
> > > -                if (j + 5 > rows)<br>
> > > +                if (on_tty() && j + 5 > rows)<br>
> > >                          break;<br>
> > ><br>
> > >                  g = array[j];<br>
> > > @@ -484,10 +515,13 @@ static int display(Hashmap *a) {<br>
> > >                  else<br>
> > >                          fputs("       -", stdout);<br>
> > ><br>
> > > -                if (g->cpu_valid)<br>
> > > -                        printf(" %6.1f", g->cpu_fraction*100);<br>
> > > +                if (arg_cpu_type == CPU_PERCENT)<br>
> > > +                        if (g->cpu_valid)<br>
> > > +                                printf(" %6.1f", g->cpu_fraction*100);<br>
> > > +                        else<br>
> > > +                                fputs("      -", stdout);<br>
> > >                  else<br>
> > > -                        fputs("      -", stdout);<br>
> > > +                        printf(" %*llu", maxtcpu, g->cpu_usage);<br>
> > ><br>
> > >                  if (g->memory_valid)<br>
> > >                          printf(" %8s", format_bytes(m, sizeof(m),<br>
> > g->memory));<br>
> > > @@ -519,6 +553,7 @@ static void help(void) {<br>
> > >                 "  -c                  Order by CPU load\n"<br>
> > >                 "  -m                  Order by memory load\n"<br>
> > >                 "  -i                  Order by IO load\n"<br>
> > > +               "     --cpu=TYPE       time or percentage (default) as<br>
> > CPU type\n"<br>
> > >                 "  -d --delay=DELAY    Specify delay\n"<br>
> > >                 "  -n --iterations=N   Run for N iterations before<br>
> > exiting\n"<br>
> > >                 "  -b --batch          Run in batch mode, accepting no<br>
> > input\n"<br>
> > > @@ -535,6 +570,7 @@ static int parse_argv(int argc, char *argv[]) {<br>
> > >          enum {<br>
> > >                  ARG_VERSION = 0x100,<br>
> > >                  ARG_DEPTH,<br>
> > > +                ARG_CPU_TYPE<br>
> > >          };<br>
> > ><br>
> > >          static const struct option options[] = {<br>
> > > @@ -544,6 +580,7 @@ static int parse_argv(int argc, char *argv[]) {<br>
> > >                  { "iterations", required_argument, NULL, 'n'         },<br>
> > >                  { "batch",      no_argument,       NULL, 'b'         },<br>
> > >                  { "depth",      required_argument, NULL, ARG_DEPTH   },<br>
> > > +                { "cpu",        required_argument, NULL, ARG_CPU_TYPE},<br>
> > I think this should be optional_argument.<br>
> ><br>
> Umut: Didn't quite understand this. Please explain why you think it should<br>
> be optional?<br>
</div></div>Because --cpu=percentange is the default. So the part after = (arg),<br>
should be optional.<br>
<div><div class="h5"><br>
> > >                  { NULL,         0,                 NULL, 0           }<br>
> > >          };<br>
> > ><br>
> > > @@ -565,6 +602,11 @@ static int parse_argv(int argc, char *argv[]) {<br>
> > >                          version();<br>
> > >                          return 0;<br>
> > ><br>
> > > +                case ARG_CPU_TYPE:<br>
> > > +                        if (strcmp(optarg, "time") == 0)<br>
> > > +                                arg_cpu_type = CPU_TIME;<br>
> > > +                        break;<br>
> > This should check for errors, especially that we want cgtop to be<br>
> > scriptable. So an unsupported type should result in an error.<br>
> ><br>
> Umut: I will add the type limitation.<br>
><br>
> ><br>
> > >                  case ARG_DEPTH:<br>
> > >                          r = safe_atou(optarg, &arg_depth);<br>
> > >                          if (r < 0) {<br>
> > > @@ -777,8 +819,6 @@ int main(int argc, char *argv[]) {<br>
> > >                  }<br>
> > >          }<br>
> > ><br>
> > > -        log_info("Exiting.");<br>
> > > -<br>
> > >          r = 0;<br>
> > I thought that it was supposed to break off after one loop, if !on_tty().<br>
> > It seems to loop. Also, for me is still prints too much headers:<br>
> ><br>
> Umut: I thought not to put a 1 loop limitation when not on tty. Someone<br>
> might want to record data over a period of time. Thats why I kept the<br>
> limit. I have also noticed the repeating column headers but the reason why<br>
> I kept them was, to provide a splitter between consecutive data output. If<br>
> the consensus is only printing them once, I will fix it.<br>
</div></div>Oh, OK. Would have nice to have this in the message description.<br>
The headers are probably not useful, if anything the empty line<br>
between consecutive outputs is good enough.<br>
<br>
I'm not convinced about the loop, though. It would not be very easy<br>
to use in scripting, because one would have to split the output. Actually<br>
invoking cgtop multiple times. Do we actually have a concrete example<br>
how this is to be used?<br>
<div><div class="h5"><br>
> > $ systemd-cgtop|cat<br>
> > Path                                 Tasks   %CPU   Memory  Input/s<br>
> > Output/s<br>
> ><br>
> > /                                      395   18.8     2.7G        -<br>
> >  -<br>
> > ...<br>
> ><br>
> > I guess that you might be developing on 32 bits. Please put something like<br>
> > this on top:<br>
> ><br>
> Umut: I am indeed on 32 bit. I will use the format modifiers from stdint.<br>
><br>
> ><br>
> > diff --git src/cgtop/cgtop.c src/cgtop/cgtop.c<br>
> > index 0ef4fcc..9f7ffd6 100644<br>
> > --- src/cgtop/cgtop.c<br>
> > +++ src/cgtop/cgtop.c<br>
> > @@ -19,9 +19,11 @@<br>
> >    along with systemd; If not, see <<a href="http://www.gnu.org/licenses/" target="_blank">http://www.gnu.org/licenses/</a>>.<br>
> >  ***/<br>
> ><br>
> > +#define __STDC_FORMAT_MACROS<br>
> >  #include <errno.h><br>
> >  #include <string.h><br>
> >  #include <stdlib.h><br>
> > +#include <stdint.h><br>
> >  #include <unistd.h><br>
> >  #include <alloca.h><br>
> >  #include <getopt.h><br>
> > @@ -460,7 +462,7 @@ static int display(Hashmap *a) {<br>
> ><br>
> >          /* Find longest names in one run */<br>
> >          for (j = 0; j < n; j++) {<br>
> > -                snprintf(cpu_title, sizeof(cpu_title), "%llu",<br>
> > array[j]->cpu_usage);<br>
> > +                snprintf(cpu_title, sizeof(cpu_title), "%"PRIu64,<br>
> > array[j]->cpu_usage);<br>
> >                  if (strlen(cpu_title) > maxtcpu)<br>
> >                          maxtcpu = strlen(cpu_title);<br>
> >                  if (strlen(array[j]->path) > maxtpath)<br>
> > @@ -521,7 +523,7 @@ static int display(Hashmap *a) {<br>
> >                          else<br>
> >                                  fputs("      -", stdout);<br>
> >                  else<br>
> > -                        printf(" %*llu", maxtcpu, g->cpu_usage);<br>
> > +                        printf(" %*"PRIu64, maxtcpu, g->cpu_usage);<br>
> ><br>
> >                  if (g->memory_valid)<br>
> >                          printf(" %8s", format_bytes(m, sizeof(m),<br>
<br>
</div></div>Zbyszek<br>
</blockquote></div><br></div>