Discussion:
[PATCH 0/4] 2.1 Show User's Processes
Sergey Senozhatsky
2011-06-06 11:50:59 UTC
Permalink
[2.1 SUP]

Attepmt to implement
-- have a "show me my process" page

There is no separate page or tab for user's processes, everything is
shown on 'Overview' tab. The solution is to have in all_processes/all_power
only processes with owner's ruid matched to the given one by user
(via 'u' switch).

For now we have too big measurement sleep interval, under some kind of
loads huge number of processes are gone by the time we initiate corresponding
event processing. This leads to a fact, that /proc/PID/* files can't be
read when we create a process, so we can't find owner's ruid (/proc/PID/status).
In such a situation (with uid filtering enabled) the only sane (to my mind)
solution is to consider such 'orphan' processes being illegal and not let them
get into statistics (all_power).

In order to have more relevant statistics we also should let user set
measurement timer (like top does via '-s').

Also available at
git://github.com/sergey-senozhatsky/powertop2-ss.git -b 2.1-processes-by-uid
Sergey Senozhatsky
2011-06-06 11:53:57 UTC
Permalink
[2.1 SUP]

uid is stored in static uid_t variable. Default value is
(uid_t)-1.

Introduce library routines:
-- get_user_input()
Read STDIN limited in size to sz via getnstr().

-- login_to_uid()
Convert user's input from UID/login name to uid_t and store
result in static uid.

-- match_uid()
Match passed ruid with uid. If uid is in default state ((uid_t)-1)
return 1.


Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky-***@public.gmane.org>

---

lib.cpp | 41 +++++++++++++++++++++++++++++++++++++++++
lib.h | 4 ++++
2 files changed, 45 insertions(+), 0 deletions(-)

diff --git a/lib.cpp b/lib.cpp
index 368212e..767a006 100644
--- a/lib.cpp
+++ b/lib.cpp
@@ -54,7 +54,10 @@ extern "C" {
#include <libintl.h>
#include <limits>
#include <math.h>
+#include <pwd.h>
+#include <ncurses.h>

+static uid_t uid = -1;
static int kallsyms_read = 0;

double percentage(double F)
@@ -391,3 +394,41 @@ void process_directory(const char *d_name, callback fn)
closedir(dir);
}

+int get_user_input(char *buf, unsigned sz)
+{
+ fflush(stdout);
+ echo();
+ /* Upon successful completion, these functions return OK. Otherwise, they return ERR. */
+ int ret = getnstr(buf, sz);
+ noecho();
+ fflush(stdout);
+ /* to distinguish between getnstr error and empty line */
+ return ret || strlen(buf);
+}
+
+void login_to_uid(const char *str)
+{
+ struct passwd *passwd_data;
+ char *endp;
+
+ /* user can supply UID or login name */
+ uid = strtoul(str, &endp, 0);
+ /* login name to uid */
+ if(*endp != 0x00) {
+ passwd_data = getpwnam(str);
+ if (!passwd_data) {
+ /* disable filtering */
+ uid = -1;
+ return;
+ }
+ uid = passwd_data->pw_uid;
+ }
+}
+
+bool match_uid(uid_t id)
+{
+ /* case (uid_t)-1 means that filtering is disabled. */
+ if (uid == (uid_t)-1)
+ return 1;
+ return uid == id;
+}
diff --git a/lib.h b/lib.h
index 39221a9..9c097ad 100644
--- a/lib.h
+++ b/lib.h
@@ -75,5 +75,9 @@ typedef void (*callback)(const char*);
extern void process_directory(const char *d_name, callback fn);
extern int utf_ok;

+extern int get_user_input(char *buf, unsigned sz);
+extern void login_to_uid(const char *str);
+extern bool match_uid(uid_t uid);
+
#endif
Sergey Senozhatsky
2011-06-06 11:56:31 UTC
Permalink
[2.1 SUP]

find_create_process() can return NULL if ruid of the process didn't match
given uid. Work around this case.

Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky-***@public.gmane.org>

---

process/do_process.cpp | 9 ++++++++-
1 files changed, 8 insertions(+), 1 deletions(-)

diff --git a/process/do_process.cpp b/process/do_process.cpp
index bf7ea23..60a9adc 100644
--- a/process/do_process.cpp
+++ b/process/do_process.cpp
@@ -234,6 +234,10 @@ void perf_process_bundle::handle_trace_point(int type, void *trace, int cpu, uin
if (consumer_depth(cpu))
pop_consumer(cpu);

+ /* If only processes with matching uid should be processed. */
+ if (!new_proc)
+ return;
+
push_consumer(cpu, new_proc);

/* start new process */
@@ -285,6 +289,10 @@ void perf_process_bundle::handle_trace_point(int type, void *trace, int cpu, uin
from_proc = (class process *) from;
}

+ /* If only processes with matching uid should be processed. */
+ if (!dest_proc)
+ return;
+
if (from_proc && (dest_proc->running == 0) && (dest_proc->waker == NULL) && (we->pid != 0) && !dont_blame_me(from_proc->comm))
dest_proc->waker = from;
if (from)
@@ -945,7 +953,6 @@ double total_cpu_time(void)
total += all_power[i]->accumulated_runtime - all_power[i]->child_runtime;
}

-
total = (total / (0.0001 + last_stamp - first_stamp));

return total;
Sergey Senozhatsky
2011-06-06 12:00:53 UTC
Permalink
[2.1 SUP]
Introduce uid_t ruid member variable -- owner's real uid.
By default ruid should have (uid_t)-1 value.

Move /proc/PID/* parsing logic out from constructor to separate
init_process() function, returning parsed ruid value. Due to events
processing delay (in measurement) process (and corresponding /proc/PID/*
files) may went away by the time we start processing. In this case we can't
find process owner's ruid, so (uid_t)-1 guarantees that such process will
not get into all_processes/all_power.

Change find_create_process() so it can return NULL if process owner's
ruid is not matched to user supplied uid. This also implies that
sched:sched_switch and sched:sched_wakeup events will be processed only
for user's processes.


Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky-***@public.gmane.org>

---

process/process.cpp | 92 ++++++++++++++++++++++++++++++++++----------------
process/process.h | 5 ++-
2 files changed, 65 insertions(+), 32 deletions(-)

diff --git a/process/process.cpp b/process/process.cpp
index 0b0f788..45e79a4 100644
--- a/process/process.cpp
+++ b/process/process.cpp
@@ -82,45 +82,47 @@ static void cmdline_to_string(char *str)
}
}

-
-process::process(const char *_comm, int _pid, int _tid) : power_consumer()
+/* return processes uid or (uid_t)-1 if not parsed.
+ */
+uid_t process::init_process()
{
char line[4096];
ifstream file;

- strcpy(comm, _comm);
- pid = _pid;
- is_idle = 0;
- running = 0;
- last_waker = NULL;
- waker = NULL;
- is_kernel = 0;
- tgid = _tid;
-
- if (_tid == 0) {
- sprintf(line, "/proc/%i/status", _pid);
- file.open(line);
- while (file) {
- file.getline(line, 4096);
- if (strstr(line, "Tgid")) {
- char *c;
- c = strchr(line, ':');
- if (!c)
- continue;
- c++;
- tgid = strtoull(c, NULL, 10);
- break;
- }
+ /* Process may be gone by the time we reach this path due
+ * to sleep in measurement. The following two reads may fail as
+ * a result.
+ */
+ char to_read = 2;
+ sprintf(line, "/proc/%i/status", pid);
+ file.open(line);
+ while (file) {
+ if (to_read < 1) break;
+ file.getline(line, 4096);
+ if (tgid == 0 && strstr(line, "Tgid")) {
+ char *c;
+ c = strchr(line, ':');
+ if (!c)
+ continue;
+ c++;
+ tgid = strtoull(c, NULL, 10);
+ --to_read;
+ continue;
+ }
+ if (strstr(line, "Uid")) {
+ /* get real UID */
+ /* sscanf(line, "Uid: %d %d %d %d", &ruid, &euid, &suid, &fuid); */
+ sscanf(line, "Uid: %d", &ruid);
+ --to_read;
+ continue;
}
- file.close();
}
+ file.close();

- if (strncmp(_comm, "kondemand/", 10) == 0)
+ if (strncmp(comm, "kondemand/", 10) == 0)
is_idle = 1;

- strcpy(desc, comm);
-
- sprintf(line, "/proc/%i/cmdline", _pid);
+ sprintf(line, "/proc/%i/cmdline", pid);
file.open(line, ios::binary);
if (file) {
memset(line, 0, sizeof(line));
@@ -136,6 +138,26 @@ process::process(const char *_comm, int _pid, int _tid) : power_consumer()
desc[sz] = 0x00;
}
}
+
+ return ruid;
+}
+
+process::process(const char *_comm, int _pid, int _tid) : power_consumer()
+{
+ strcpy(comm, _comm);
+ strcpy(desc, comm);
+ pid = _pid;
+ is_idle = 0;
+ running = 0;
+ last_waker = NULL;
+ waker = NULL;
+ is_kernel = 0;
+ tgid = _tid;
+ /* Since process may already went away, setting ruid to -1
+ * will guarantee that 'orphan' event will not get into
+ * user's statistics.
+ */
+ ruid = -1;
}

const char * process::description(void)
@@ -169,6 +191,16 @@ class process * find_create_process(char *comm, int pid)
return it->second;

new_proc = new class process(comm, pid);
+ /* init_process will return uid (if parsed). If uid has
+ * been set by user, only processes with matched uid will
+ * get into all_processes. This also implies that sched:sched_switch
+ * and sched:sched_wakeup events will be processed only for user's
+ * processes.
+ */
+ if ( !match_uid(new_proc->init_process()) ) {
+ delete new_proc;
+ return NULL;
+ }

/* The trick is to use process->comm as the key. comm has the same
* life-time as the corresponding process does, so we can avoid
diff --git a/process/process.h b/process/process.h
index 777e90d..181be5c 100644
--- a/process/process.h
+++ b/process/process.h
@@ -29,6 +29,7 @@
#include <map>
#include <string.h>
#include "powerconsumer.h"
+#include "../lib.h"

#ifdef __x86_64__
#define BIT64 1
@@ -47,14 +48,14 @@ public:
int tgid;
char comm[16];
int pid;
-
+ uid_t ruid;

int is_idle; /* count this as if the cpu was idle */
int running;
int is_kernel; /* kernel thread */

process(const char *_comm, int _pid, int _tid = 0);
-
+ uid_t init_process();
virtual void schedule_thread(uint64_t time, int thread_id);
virtual uint64_t deschedule_thread(uint64_t time, int thread_id = 0);
Sergey Senozhatsky
2011-06-06 11:58:55 UTC
Permalink
[2.1 SUP]

Introduce 'u' switch to activate set_uid(), that will ask for
user login or uid. Blank line as login name indicates that uid filtering
should be disabled (show processes for all users).

Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky-***@public.gmane.org>

---

main.cpp | 15 +++++++++++++++
1 files changed, 15 insertions(+), 0 deletions(-)

diff --git a/main.cpp b/main.cpp
index 6c7628e..fae1b0b 100644
--- a/main.cpp
+++ b/main.cpp
@@ -75,6 +75,18 @@ static void print_version()
printf(_("Powertop version" POWERTOP_VERSION ", compiled on "__DATE__ "\n"));
}

+static void set_uid()
+{
+ static char buf[32];
+
+ mvprintw(1, 0, "%s", _("User login name or ID (blank for all): "));
+ memset(buf, '\0', sizeof(buf));
+ if (get_user_input(buf, sizeof(buf) - 1) < 1)
+ strcpy(buf, "-1");
+ login_to_uid(buf);
+ show_tab(0);
+}
+
static void print_usage()
{
printf(_("Usage: powertop [OPTIONS]\n\n"));
@@ -126,6 +138,9 @@ static void do_sleep(int seconds)
case 10:
cursor_enter();
break;
+ case 'u':
+ set_uid();
+ break;
case 'r':
window_refresh();
break;

Loading...