mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
363 lines
8.8 KiB
C
363 lines
8.8 KiB
C
/* sysprofd.c
|
|
*
|
|
* Copyright (C) 2016 Christian Hergert <christian@hergert.me>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <linux/capability.h>
|
|
#include <linux/perf_event.h>
|
|
#include <stdlib.h>
|
|
#include <sys/syscall.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "sd-bus-helper.h"
|
|
|
|
#define BUS_TIMEOUT_USEC (1000000L * 10L)
|
|
|
|
static int
|
|
_perf_event_open (struct perf_event_attr *attr,
|
|
pid_t pid,
|
|
int cpu,
|
|
int group_fd,
|
|
unsigned long flags)
|
|
{
|
|
assert (attr != NULL);
|
|
|
|
/* Quick sanity check */
|
|
if (attr->sample_period < 100000)
|
|
return -EINVAL;
|
|
|
|
return syscall (__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
|
|
}
|
|
|
|
#if 0
|
|
#define GOTO(l) do { \
|
|
fprintf (stderr, "GOTO: %s:%d: " #l "\n", __FUNCTION__, __LINE__); \
|
|
goto l; \
|
|
} while (0)
|
|
#else
|
|
#define GOTO(l) goto l
|
|
#endif
|
|
|
|
static int
|
|
sysprofd_perf_event_open (sd_bus_message *msg,
|
|
void *user_data,
|
|
sd_bus_error *error)
|
|
{
|
|
struct perf_event_attr attr = { 0 };
|
|
sd_bus_message *reply = NULL;
|
|
uint64_t flags = 0;
|
|
int disabled = 0;
|
|
int32_t wakeup_events = 149;
|
|
int32_t cpu = -1;
|
|
int32_t pid = -1;
|
|
bool challenge = false;
|
|
int32_t type = 0;
|
|
uint64_t sample_period = 0;
|
|
uint64_t sample_type = 0;
|
|
uint64_t config = 0;
|
|
int clockid = CLOCK_MONOTONIC_RAW;
|
|
int comm = 0;
|
|
int mmap_ = 0;
|
|
int task = 0;
|
|
int exclude_idle = 0;
|
|
int fd = -1;
|
|
int use_clockid = 0;
|
|
int sample_id_all = 0;
|
|
int r;
|
|
|
|
assert (msg);
|
|
|
|
r = sd_bus_message_enter_container (msg, SD_BUS_TYPE_ARRAY, "{sv}");
|
|
if (r < 0)
|
|
return r;
|
|
|
|
for (;;)
|
|
{
|
|
const char *name = NULL;
|
|
|
|
r = sd_bus_message_enter_container (msg, SD_BUS_TYPE_DICT_ENTRY, "sv");
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (r == 0)
|
|
break;
|
|
|
|
r = sd_bus_message_read (msg, "s", &name);
|
|
if (r < 0)
|
|
goto cleanup;
|
|
|
|
r = sd_bus_message_enter_container (msg, SD_BUS_TYPE_VARIANT, NULL);
|
|
if (r < 0)
|
|
goto cleanup;
|
|
|
|
if (strcmp (name, "disabled") == 0)
|
|
{
|
|
r = sd_bus_message_read (msg, "b", &disabled);
|
|
if (r < 0)
|
|
GOTO (cleanup);
|
|
}
|
|
else if (strcmp (name, "wakeup_events") == 0)
|
|
{
|
|
r = sd_bus_message_read (msg, "u", &wakeup_events);
|
|
if (r < 0)
|
|
GOTO (cleanup);
|
|
}
|
|
else if (strcmp (name, "sample_id_all") == 0)
|
|
{
|
|
r = sd_bus_message_read (msg, "b", &sample_id_all);
|
|
if (r < 0)
|
|
GOTO (cleanup);
|
|
}
|
|
else if (strcmp (name, "clockid") == 0)
|
|
{
|
|
r = sd_bus_message_read (msg, "i", &clockid);
|
|
if (r < 0)
|
|
GOTO (cleanup);
|
|
}
|
|
else if (strcmp (name, "comm") == 0)
|
|
{
|
|
r = sd_bus_message_read (msg, "b", &comm);
|
|
if (r < 0)
|
|
GOTO (cleanup);
|
|
}
|
|
else if (strcmp (name, "exclude_idle") == 0)
|
|
{
|
|
r = sd_bus_message_read (msg, "b", &exclude_idle);
|
|
if (r < 0)
|
|
GOTO (cleanup);
|
|
}
|
|
else if (strcmp (name, "mmap") == 0)
|
|
{
|
|
r = sd_bus_message_read (msg, "b", &mmap_);
|
|
if (r < 0)
|
|
GOTO (cleanup);
|
|
}
|
|
else if (strcmp (name, "config") == 0)
|
|
{
|
|
r = sd_bus_message_read (msg, "t", &config);
|
|
if (r < 0)
|
|
GOTO (cleanup);
|
|
}
|
|
else if (strcmp (name, "sample_period") == 0)
|
|
{
|
|
r = sd_bus_message_read (msg, "t", &sample_period);
|
|
if (r < 0)
|
|
GOTO (cleanup);
|
|
}
|
|
else if (strcmp (name, "sample_type") == 0)
|
|
{
|
|
r = sd_bus_message_read (msg, "t", &sample_type);
|
|
if (r < 0)
|
|
GOTO (cleanup);
|
|
}
|
|
else if (strcmp (name, "task") == 0)
|
|
{
|
|
r = sd_bus_message_read (msg, "b", &task);
|
|
if (r < 0)
|
|
GOTO (cleanup);
|
|
}
|
|
else if (strcmp (name, "type") == 0)
|
|
{
|
|
r = sd_bus_message_read (msg, "u", &type);
|
|
if (r < 0)
|
|
GOTO (cleanup);
|
|
}
|
|
else if (strcmp (name, "use_clockid") == 0)
|
|
{
|
|
r = sd_bus_message_read (msg, "b", &use_clockid);
|
|
if (r < 0)
|
|
GOTO (cleanup);
|
|
}
|
|
|
|
r = sd_bus_message_exit_container (msg);
|
|
if (r < 0)
|
|
goto cleanup;
|
|
|
|
sd_bus_message_exit_container (msg);
|
|
if (r < 0)
|
|
goto cleanup;
|
|
|
|
cleanup:
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
r = sd_bus_message_exit_container (msg);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = sd_bus_message_read (msg, "iit", &pid, &cpu, &flags);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (pid < -1 || cpu < -1)
|
|
return -EINVAL;
|
|
|
|
r = sd_bus_message_new_method_return (msg, &reply);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
/* Authorize peer */
|
|
r = bus_test_polkit (msg,
|
|
CAP_SYS_ADMIN,
|
|
"org.gnome.sysprof2.perf-event-open",
|
|
NULL,
|
|
UID_INVALID,
|
|
&challenge,
|
|
error);
|
|
if (r < 0)
|
|
return r;
|
|
else if (r == 0)
|
|
return -EACCES;
|
|
|
|
attr.comm = !!comm;
|
|
attr.config = config;
|
|
attr.disabled = disabled;
|
|
attr.exclude_idle = !!exclude_idle;
|
|
attr.mmap = !!mmap_;
|
|
attr.sample_id_all = sample_id_all;
|
|
attr.sample_period = sample_period;
|
|
attr.sample_type = sample_type;
|
|
attr.task = !!task;
|
|
attr.type = type;
|
|
attr.wakeup_events = wakeup_events;
|
|
|
|
#ifdef HAVE_PERF_CLOCKID
|
|
if (!use_clockid || clockid < 0)
|
|
attr.clockid = CLOCK_MONOTONIC_RAW;
|
|
else
|
|
attr.clockid = clockid;
|
|
attr.use_clockid = use_clockid;
|
|
#endif
|
|
|
|
attr.size = sizeof attr;
|
|
|
|
fd = _perf_event_open (&attr, pid, cpu, -1, 0);
|
|
if (fd < 0)
|
|
{
|
|
fprintf (stderr,
|
|
"Failed to open perf event stream: %s\n",
|
|
strerror (errno));
|
|
return -EINVAL;
|
|
}
|
|
|
|
sd_bus_message_append_basic (reply, SD_BUS_TYPE_UNIX_FD, &fd);
|
|
r = sd_bus_send (NULL, reply, NULL);
|
|
sd_bus_message_unref (reply);
|
|
|
|
close (fd);
|
|
|
|
return r;
|
|
}
|
|
|
|
static const sd_bus_vtable sysprofd_vtable[] = {
|
|
SD_BUS_VTABLE_START (0),
|
|
SD_BUS_METHOD ("PerfEventOpen", "a{sv}iit", "h", sysprofd_perf_event_open, SD_BUS_VTABLE_UNPRIVILEGED),
|
|
SD_BUS_VTABLE_END
|
|
};
|
|
|
|
int
|
|
main (int argc,
|
|
char *argv[])
|
|
{
|
|
sd_bus_slot *slot = NULL;
|
|
sd_bus *bus = NULL;
|
|
int r;
|
|
|
|
/* Connect to the system bus */
|
|
r = sd_bus_default_system (&bus);
|
|
if (r < 0)
|
|
{
|
|
fprintf (stderr,
|
|
"Failed to connect to system bus: %s\n",
|
|
strerror (-r));
|
|
goto failure;
|
|
}
|
|
|
|
/* Install our object */
|
|
r = sd_bus_add_object_vtable (bus,
|
|
&slot,
|
|
"/org/gnome/Sysprof2",
|
|
"org.gnome.Sysprof2",
|
|
sysprofd_vtable,
|
|
NULL);
|
|
if (r < 0)
|
|
{
|
|
fprintf (stderr,
|
|
"Failed to install object on bus: %s\n",
|
|
strerror (-r));
|
|
goto failure;
|
|
}
|
|
|
|
/* Request our well-known name on the bus */
|
|
r = sd_bus_request_name (bus, "org.gnome.Sysprof2", 0);
|
|
if (r < 0)
|
|
{
|
|
fprintf (stderr,
|
|
"Failed to register name on the bus: %s\n",
|
|
strerror (-r));
|
|
goto failure;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
/* Process requests */
|
|
r = sd_bus_process (bus, NULL);
|
|
if (r < 0)
|
|
{
|
|
fprintf (stderr,
|
|
"Failed to process bus: %s\n",
|
|
strerror (-r));
|
|
goto failure;
|
|
}
|
|
|
|
/* If we processed a request, continue processing */
|
|
if (r > 0)
|
|
continue;
|
|
|
|
/* Wait for the next request to process */
|
|
r = sd_bus_wait (bus, BUS_TIMEOUT_USEC);
|
|
if (r < 0)
|
|
{
|
|
fprintf (stderr,
|
|
"Failed to wait on bus: %s\n",
|
|
strerror (-r));
|
|
goto failure;
|
|
}
|
|
|
|
/*
|
|
* If we timed out, we can expire, we will be auto-started by
|
|
* systemd or dbus on the next activation request.
|
|
*/
|
|
if (r == 0)
|
|
break;
|
|
}
|
|
|
|
failure:
|
|
sd_bus_slot_unref (slot);
|
|
sd_bus_unref (bus);
|
|
|
|
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
|
}
|