Replace zu with PRIsz and llu with PRIu64.
src/liboconfig/aux_types.h \
src/liboconfig/scanner.l \
src/liboconfig/parser.y
+liboconfig_la_CPPFLAGS = -I$(srcdir)/src/liboconfig $(AM_CPPFLAGS)
liboconfig_la_LDFLAGS = -avoid-version $(LEXLIB)
python_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBPYTHON_LDFLAGS)
endif
+if HAVE_LIBMNL
+noinst_LTLIBRARIES += libtaskstats.la
+libtaskstats_la_SOURCES = \
+ src/utils_taskstats.c \
+ src/utils_taskstats.h
+libtaskstats_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMNL_CFLAGS)
+libtaskstats_la_LIBADD = $(BUILD_WITH_LIBMNL_LIBS)
+endif
+
if BUILD_PLUGIN_PROCESSES
pkglib_LTLIBRARIES += processes.la
processes_la_SOURCES = src/processes.c
+processes_la_CPPFLAGS = $(AM_CPPFLAGS)
processes_la_LDFLAGS = $(PLUGIN_LDFLAGS)
processes_la_LIBADD =
if BUILD_WITH_LIBKVM_GETPROCS
processes_la_LIBADD += -lkvm
endif
+if HAVE_LIBMNL
+processes_la_CPPFLAGS += -DHAVE_LIBTASKSTATS=1
+processes_la_LIBADD += libtaskstats.la
+endif
endif
if BUILD_PLUGIN_PROTOCOLS
if test "x$withval" != "xno" && test "x$withval" != "xyes"; then
with_libgrpcpp_cppflags="-I$withval/include"
with_libgrpcpp_ldflags="-L$withval/lib"
+ with_libgrpcpp_bin="$withval/bin"
with_libgrpcpp="yes"
fi
if test "x$withval" = "xno"; then
# }}}
AC_ARG_VAR([GRPC_CPP_PLUGIN], [path to the grpc_cpp_plugin binary])
-AC_PATH_PROG([GRPC_CPP_PLUGIN], [grpc_cpp_plugin])
+if test "x$with_libgrpcpp_bin" = "x"; then
+ AC_PATH_PROG([GRPC_CPP_PLUGIN], [grpc_cpp_plugin])
+else
+ AC_PATH_PROG([GRPC_CPP_PLUGIN], [grpc_cpp_plugin], [], "$with_libgrpcpp_bin:$PATH")
+fi
AM_CONDITIONAL([HAVE_GRPC_CPP], [test "x$GRPC_CPP_PLUGIN" != "x"])
# --with-libiptc {{{
fi
AC_SUBST([BUILD_WITH_LIBMNL_CFLAGS])
AC_SUBST([BUILD_WITH_LIBMNL_LIBS])
+AM_CONDITIONAL([HAVE_LIBMNL], [test "x$with_libmnl" = "xyes"])
# }}}
# --with-libnetapp {{{
plugin_gps="yes"
fi
-if test "x$with_libgrpcpp" = "xyes" && test "x$with_libprotobuf" = "xyes" && test "x$have_protoc3" = "xyes" && test "x$GRPC_CPP_PLUGIN" != "x"; then
- plugin_grpc="yes"
+plugin_grpc="yes"
+if test "x$GRPC_CPP_PLUGIN" = "x"; then
+ plugin_grpc="no (grpc_cpp_plugin not found)"
+fi
+if test "x$have_protoc3" != "xyes"; then
+ plugin_grpc="no (protoc3 not found)"
+fi
+if test "x$with_libprotobuf" != "xyes"; then
+ plugin_grpc="no (libprotobuf not found)"
+fi
+if test "x$with_libgrpcpp" != "xyes"; then
+ plugin_grpc="no (libgrpc++ not found)"
fi
if test "x$have_getifaddrs" = "xyes"; then
# intel_pmu CAP_SYS_ADMIN
# iptables CAP_NET_ADMIN
# ping CAP_NET_RAW
+# processes CAP_NET_ADMIN (CollectDelayAccounting only)
# smart CAP_SYS_RAWIO
# turbostat CAP_SYS_RAWIO
#
# SSLCACertificateFile "/path/to/root.pem"
# SSLCertificateFile "/path/to/client.pem"
# SSLCertificateKeyFile "/path/to/client.key"
+# VerifyPeer true
# </Listen>
#</Plugin>
# CollectFileDescriptor true
# CollectContextSwitch true
# CollectMemoryMaps true
+# CollectDelayAccounting false
# Process "name"
# ProcessMatch "name" "regex"
# <Process "collectd">
# CollectFileDescriptor false
# CollectContextSwitch false
+# CollectDelayAccounting true
# </Process>
# <ProcessMatch "name" "regex">
# CollectFileDescriptor false
Filenames specifying SSL certificate and key material to be used with SSL
connections.
+=item B<VerifyPeer> B<true>|B<false>
+
+When enabled, a valid client certificate is required to connect to the server.
+When disabled, a client certifiacte is not requested and any unsolicited client
+certificate is accepted.
+Enabled by default.
+
=back
=back
- number of memory mapped files (under Linux)
- io data (where available)
- context switches (under Linux)
- - minor and major pagefaults.
+ - minor and major pagefaults
+ - Delay Accounting information (Linux only, requires libmnl)
B<Synopsis:>
<Plugin processes>
- CollectFileDescriptor true
- CollectContextSwitch true
+ CollectFileDescriptor true
+ CollectContextSwitch true
+ CollectDelayAccounting false
Process "name"
ProcessMatch "name" "regex"
<Process "collectd">
- CollectFileDescriptor false
- CollectContextSwitch false
+ CollectFileDescriptor false
+ CollectContextSwitch false
+ CollectDelayAccounting true
</Process>
<ProcessMatch "name" "regex">
CollectFileDescriptor false
- CollectContextSwitch true
+ CollectContextSwitch true
</Process>
</Plugin>
Collect the number of context switches for matched processes.
Disabled by default.
+=item B<CollectDelayAccounting> I<Boolean>
+
+If enabled, collect Linux Delay Accounding information for matching processes.
+Delay Accounting provides the time processes wait for the CPU to become
+available, for I/O operations to finish, for pages to be swapped in and for
+freed pages to be reclaimed. The metrics are reported as "seconds per second"
+using the C<delay_rate> type, e.g. C<delay_rate-delay-cpu>.
+Disabled by default.
+
+This option is only available on Linux, requires the C<libmnl> library and
+requires the C<CAP_NET_ADMIN> capability at runtime.
+
=item B<CollectFileDescriptor> I<Boolean>
Collect number of file descriptors of matched processes.
=back
-Options B<CollectContextSwitch> and B<CollectFileDescriptor> may be used inside
-B<Process> and B<ProcessMatch> blocks - then they affect corresponding match
-only. Otherwise they set the default value for subsequent matches.
+The B<CollectContextSwitch>, B<CollectDelayAccounting>,
+B<CollectFileDescriptor> and B<CollectMemoryMaps> options may be used inside
+B<Process> and B<ProcessMatch> blocks. When used there, these options affect
+reporting the corresponding processes only. Outside of B<Process> and
+B<ProcessMatch> blocks these options set the default value for subsequent
+matches.
=head2 Plugin C<protocols>
#elif HAVE_STATFS
struct statfs statbuf;
#endif
+ int retval = 0;
/* struct STATANYFS statbuf; */
cu_mount_t *mnt_list;
(gauge_t)((float_t)(blk_reserved) / statbuf.f_blocks * 100));
df_submit_one(disk_name, "percent_bytes", "used",
(gauge_t)((float_t)(blk_used) / statbuf.f_blocks * 100));
- } else
- return -1;
+ } else {
+ retval = -1;
+ break;
+ }
}
/* inode handling */
df_submit_one(
disk_name, "percent_inodes", "used",
(gauge_t)((float_t)(inode_used) / statbuf.f_files * 100));
- } else
- return -1;
+ } else {
+ retval = -1;
+ break;
+ }
}
if (values_absolute) {
df_submit_one(disk_name, "df_inodes", "free", (gauge_t)inode_free);
cu_mount_freelist(mnt_list);
- return 0;
+ return retval;
} /* int df_read */
void module_register(void) {
}
if (line[0] == 'e') { /* e:<type>:<bytes> */
- char *ptr = NULL;
- char *type = strtok_r(line + 2, ":", &ptr);
- char *tmp = strtok_r(NULL, ":", &ptr);
- int bytes = 0;
-
- if (tmp == NULL) {
+ char *type = line + 2;
+ char *bytes_str = strchr(type, ':');
+ if (bytes_str == NULL) {
log_err("collect: syntax error in line '%s'", line);
continue;
}
- bytes = atoi(tmp);
+ *bytes_str = 0;
+ bytes_str++;
pthread_mutex_lock(&count_mutex);
type_list_incr(&list_count, type, /* increment = */ 1);
pthread_mutex_unlock(&count_mutex);
+ int bytes = atoi(bytes_str);
if (bytes > 0) {
pthread_mutex_lock(&size_mutex);
type_list_incr(&list_size, type, /* increment = */ bytes);
pthread_exit((void *)1);
}
- struct sockaddr_un addr = {.sun_family = AF_UNIX};
+ struct sockaddr_un addr = {
+ .sun_family = AF_UNIX,
+ };
sstrncpy(addr.sun_path, path, (size_t)(UNIX_PATH_MAX - 1));
errno = 0;
using google::protobuf::util::TimeUtil;
-typedef google::protobuf::Map<grpc::string, collectd::types::MetadataValue> grpcMetadata;
+typedef google::protobuf::Map<grpc::string, collectd::types::MetadataValue>
+ grpcMetadata;
/*
* private types
switch (md_type) {
case MD_TYPE_STRING:
char *md_string;
- if (meta_data_get_string(meta, key, &md_string) != 0 || md_string == nullptr) {
+ if (meta_data_get_string(meta, key, &md_string) != 0 ||
+ md_string == nullptr) {
strarray_free(meta_data_keys, meta_data_keys_len);
return grpc::Status(grpc::StatusCode::INTERNAL,
- grpc::string("missing metadata"));
+ grpc::string("missing metadata"));
}
md_value.set_string_value(md_string);
free(md_string);
if (meta_data_get_signed_int(meta, key, &int64_value) != 0) {
strarray_free(meta_data_keys, meta_data_keys_len);
return grpc::Status(grpc::StatusCode::INTERNAL,
- grpc::string("missing metadata"));
+ grpc::string("missing metadata"));
}
md_value.set_int64_value(int64_value);
break;
if (meta_data_get_unsigned_int(meta, key, &uint64_value) != 0) {
strarray_free(meta_data_keys, meta_data_keys_len);
return grpc::Status(grpc::StatusCode::INTERNAL,
- grpc::string("missing metadata"));
+ grpc::string("missing metadata"));
}
md_value.set_uint64_value(uint64_value);
break;
if (meta_data_get_double(meta, key, &double_value) != 0) {
strarray_free(meta_data_keys, meta_data_keys_len);
return grpc::Status(grpc::StatusCode::INTERNAL,
- grpc::string("missing metadata"));
+ grpc::string("missing metadata"));
}
md_value.set_double_value(double_value);
break;
if (meta_data_get_boolean(meta, key, &bool_value) != 0) {
strarray_free(meta_data_keys, meta_data_keys_len);
return grpc::Status(grpc::StatusCode::INTERNAL,
- grpc::string("missing metadata"));
+ grpc::string("missing metadata"));
}
md_value.set_bool_value(bool_value);
break;
return grpc::Status(grpc::StatusCode::RESOURCE_EXHAUSTED,
grpc::string("failed to metadata list"));
}
- for (auto kv: rpc_metadata) {
+ for (auto kv : rpc_metadata) {
auto k = kv.first.c_str();
auto v = kv.second;
break;
default:
meta_data_destroy(*md_out);
- return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
- grpc::string("Metadata of unknown type"));
+ return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+ grpc::string("Metadata of unknown type"));
}
}
return grpc::Status::OK;
break;
}
if (uc_iterator_get_meta(iter, &vl.meta) < 0) {
- status = grpc::Status(grpc::StatusCode::INTERNAL,
- grpc::string("failed to retrieve value metadata"));
+ status =
+ grpc::Status(grpc::StatusCode::INTERNAL,
+ grpc::string("failed to retrieve value metadata"));
}
value_lists->push(vl);
listener.port = grpc::string(ci->values[1].value.string);
listener.ssl = nullptr;
- auto ssl_opts = new (grpc::SslServerCredentialsOptions);
+ auto ssl_opts = new grpc::SslServerCredentialsOptions(
+ GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY);
grpc::SslServerCredentialsOptions::PemKeyCertPair pkcp = {};
bool use_ssl = false;
return -1;
}
pkcp.cert_chain = read_file(cert);
+ } else if (!strcasecmp("VerifyPeer", child->key)) {
+ _Bool verify = 0;
+ if (cf_util_get_boolean(child, &verify)) {
+ return -1;
+ }
+ ssl_opts->client_certificate_request =
+ verify ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
+ : GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE;
} else {
WARNING("grpc: Option `%s` not allowed in <%s> block.", child->key,
ci->key);
#include "aux_types.h"
static char *unquote (const char *orig);
-static int yyerror (const char *s);
+static void yyerror(const char *s);
/* Lexer variables */
extern int yylineno;
argument_list argument
{
$$ = $1;
+ oconfig_value_t *tmp = realloc($$.argument,
+ ($$.argument_num+1) * sizeof(*$$.argument));
+ if (tmp == NULL) {
+ yyerror("realloc failed");
+ YYERROR;
+ }
+ $$.argument = tmp;
+ $$.argument[$$.argument_num] = $2;
$$.argument_num++;
- $$.argument = realloc ($$.argument, $$.argument_num * sizeof (oconfig_value_t));
- $$.argument[$$.argument_num-1] = $2;
}
| argument
{
- $$.argument = malloc (sizeof (oconfig_value_t));
+ $$.argument = calloc(1, sizeof(*$$.argument));
+ if ($$.argument == NULL) {
+ yyerror("calloc failed");
+ YYERROR;
+ }
$$.argument[0] = $1;
$$.argument_num = 1;
}
option:
identifier argument_list EOL
{
- memset (&$$, '\0', sizeof ($$));
+ memset(&$$, 0, sizeof($$));
$$.key = $1;
$$.values = $2.argument;
$$.values_num = $2.argument_num;
block_begin:
OPENBRAC identifier CLOSEBRAC EOL
{
- memset (&$$, '\0', sizeof ($$));
+ memset(&$$, 0, sizeof($$));
$$.key = $2;
}
|
OPENBRAC identifier argument_list CLOSEBRAC EOL
{
- memset (&$$, '\0', sizeof ($$));
+ memset(&$$, 0, sizeof($$));
$$.key = $2;
$$.values = $3.argument;
$$.values_num = $3.argument_num;
block:
block_begin statement_list block_end
{
- if (strcmp ($1.key, $3) != 0)
+ if (strcmp($1.key, $3) != 0)
{
- printf ("block_begin = %s; block_end = %s;\n", $1.key, $3);
- yyerror ("Block not closed..\n");
- exit (1);
+ printf("block_begin = %s; block_end = %s;\n", $1.key, $3);
+ yyerror("block not closed");
+ YYERROR;
}
free ($3); $3 = NULL;
$$ = $1;
}
| block_begin block_end
{
- if (strcmp ($1.key, $2) != 0)
+ if (strcmp($1.key, $2) != 0)
{
- printf ("block_begin = %s; block_end = %s;\n", $1.key, $2);
- yyerror ("Block not closed..\n");
- exit (1);
+ printf("block_begin = %s; block_end = %s;\n", $1.key, $2);
+ yyerror("block not closed");
+ YYERROR;
}
free ($2); $2 = NULL;
$$ = $1;
$$ = $1;
if (($2.values_num > 0) || ($2.children_num > 0))
{
+ oconfig_item_t *tmp = realloc($$.statement,
+ ($$.statement_num+1) * sizeof(*tmp));
+ if (tmp == NULL) {
+ yyerror("realloc failed");
+ YYERROR;
+ }
+ $$.statement = tmp;
+ $$.statement[$$.statement_num] = $2;
$$.statement_num++;
- $$.statement = realloc ($$.statement, $$.statement_num * sizeof (oconfig_item_t));
- $$.statement[$$.statement_num-1] = $2;
}
}
| statement
{
if (($1.values_num > 0) || ($1.children_num > 0))
{
- $$.statement = malloc (sizeof (oconfig_item_t));
+ $$.statement = calloc(1, sizeof(*$$.statement));
+ if ($$.statement == NULL) {
+ yyerror("calloc failed");
+ YYERROR;
+ }
$$.statement[0] = $1;
$$.statement_num = 1;
}
entire_file:
statement_list
{
- ci_root = calloc (1, sizeof (*ci_root));
+ ci_root = calloc(1, sizeof(*ci_root));
+ if (ci_root == NULL) {
+ yyerror("calloc failed");
+ YYERROR;
+ }
ci_root->children = $1.statement;
ci_root->children_num = $1.statement_num;
}
| /* epsilon */
{
- ci_root = calloc (1, sizeof (*ci_root));
- ci_root->children = NULL;
- ci_root->children_num = 0;
+ ci_root = calloc(1, sizeof(*ci_root));
+ if (ci_root == NULL) {
+ yyerror("calloc failed");
+ YYERROR;
+ }
}
;
%%
-static int yyerror (const char *s)
+static void yyerror(const char *s)
{
const char *text;
- if (*yytext == '\n')
+ if (yytext == NULL)
+ text = "<empty>";
+ else if (*yytext == '\n')
text = "<newline>";
else
text = yytext;
- fprintf (stderr, "Parse error in file `%s', line %i near `%s': %s\n",
+ fprintf(stderr, "Parse error in file `%s', line %i near `%s': %s\n",
c_file, yylineno, text, s);
- return (-1);
} /* int yyerror */
static char *unquote (const char *orig)
len -= 2;
memmove (ret, ret + 1, len);
- ret[len] = '\0';
+ ret[len] = 0;
for (int i = 0; i < len; i++)
{
close(st->fd);
st->fd = -1;
return -1;
+ } else if (status == 0) {
+ ERROR("memcached plugin: Instance \"%s\": Connection closed by peer",
+ st->name);
+ close(st->fd);
+ st->fd = -1;
+ return -1;
}
buffer_fill += (size_t)status;
/**
* collectd - src/processes.c
* Copyright (C) 2005 Lyonel Vincent
- * Copyright (C) 2006-2010 Florian octo Forster
+ * Copyright (C) 2006-2017 Florian octo Forster
* Copyright (C) 2008 Oleg King
* Copyright (C) 2009 Sebastian Harl
* Copyright (C) 2009 Andrés J. DÃaz
#include "common.h"
#include "plugin.h"
+#if HAVE_LIBTASKSTATS
+#include "utils_complain.h"
+#include "utils_taskstats.h"
+#endif
+
/* Include header files for the mach system, if they exist.. */
#if HAVE_THREAD_INFO
#if HAVE_MACH_MACH_INIT_H
#include <kstat.h>
#endif
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#endif
+
#ifndef CMDLINE_BUFFER_SIZE
#if defined(ARG_MAX) && (ARG_MAX < 4096)
#define CMDLINE_BUFFER_SIZE ARG_MAX
derive_t cswitch_invol;
_Bool has_cswitch;
+#if HAVE_LIBTASKSTATS
+ ts_delay_t delay;
+#endif
+ _Bool has_delay;
+
_Bool has_fd;
_Bool has_maps;
derive_t cswitch_vol;
derive_t cswitch_invol;
+#if HAVE_LIBTASKSTATS
+ value_to_rate_state_t delay_cpu;
+ value_to_rate_state_t delay_blkio;
+ value_to_rate_state_t delay_swapin;
+ value_to_rate_state_t delay_freepages;
+#endif
+
struct procstat_entry_s *next;
} procstat_entry_t;
derive_t cswitch_vol;
derive_t cswitch_invol;
+ /* Linux Delay Accounting. Unit is ns/s. */
+ gauge_t delay_cpu;
+ gauge_t delay_blkio;
+ gauge_t delay_swapin;
+ gauge_t delay_freepages;
+
_Bool report_fd_num;
_Bool report_maps_num;
_Bool report_ctx_switch;
+ _Bool report_delay;
struct procstat *next;
struct procstat_entry_s *instances;
static _Bool report_ctx_switch = 0;
static _Bool report_fd_num = 0;
static _Bool report_maps_num = 0;
+static _Bool report_delay = 0;
#if HAVE_THREAD_INFO
static mach_port_t port_host_self;
int getargs(void *processBuffer, int bufferLen, char *argsBuffer, int argsLen);
#endif /* HAVE_PROCINFO_H */
+#if HAVE_LIBTASKSTATS
+static ts_t *taskstats_handle = NULL;
+#endif
+
/* put name of process from config to list_head_g tree
* list_head_g is a list of 'procstat_t' structs with
* processes names we want to watch */
new->report_fd_num = report_fd_num;
new->report_maps_num = report_maps_num;
new->report_ctx_switch = report_ctx_switch;
+ new->report_delay = report_delay;
#if HAVE_REGEX_H
if (regexp != NULL) {
*group_counter += curr_value;
}
+#if HAVE_LIBTASKSTATS
+static void ps_update_delay_one(gauge_t *out_rate_sum,
+ value_to_rate_state_t *state, uint64_t cnt,
+ cdtime_t t) {
+ gauge_t rate = NAN;
+ int status = value_to_rate(&rate, (value_t){.counter = (counter_t)cnt},
+ DS_TYPE_COUNTER, t, state);
+ if ((status != 0) || isnan(rate)) {
+ return;
+ }
+
+ if (isnan(*out_rate_sum)) {
+ *out_rate_sum = rate;
+ } else {
+ *out_rate_sum += rate;
+ }
+}
+
+static void ps_update_delay(procstat_t *out, procstat_entry_t *prev,
+ process_entry_t *curr) {
+ cdtime_t now = cdtime();
+
+ ps_update_delay_one(&out->delay_cpu, &prev->delay_cpu, curr->delay.cpu_ns,
+ now);
+ ps_update_delay_one(&out->delay_blkio, &prev->delay_blkio,
+ curr->delay.blkio_ns, now);
+ ps_update_delay_one(&out->delay_swapin, &prev->delay_swapin,
+ curr->delay.swapin_ns, now);
+ ps_update_delay_one(&out->delay_freepages, &prev->delay_freepages,
+ curr->delay.freepages_ns, now);
+}
+#endif
+
/* add process entry to 'instances' of process 'name' (or refresh it) */
static void ps_list_add(const char *name, const char *cmdline,
process_entry_t *entry) {
entry->cpu_user_counter);
ps_update_counter(&ps->cpu_system_counter, &pse->cpu_system_counter,
entry->cpu_system_counter);
+
+#if HAVE_LIBTASKSTATS
+ ps_update_delay(ps, pse, entry);
+#endif
}
}
ps->vmem_code = 0;
ps->stack_size = 0;
+ ps->delay_cpu = NAN;
+ ps->delay_blkio = NAN;
+ ps->delay_swapin = NAN;
+ ps->delay_freepages = NAN;
+
pse_prev = NULL;
pse = ps->instances;
while (pse != NULL) {
cf_util_get_boolean(c, &ps->report_fd_num);
else if (strcasecmp(c->key, "CollectMemoryMaps") == 0)
cf_util_get_boolean(c, &ps->report_maps_num);
- else {
- ERROR("processes plugin: Option `%s' not allowed here.", c->key);
+ else if (strcasecmp(c->key, "CollectDelayAccounting") == 0) {
+#if HAVE_LIBTASKSTATS
+ cf_util_get_boolean(c, &ps->report_delay);
+#else
+ WARNING("processes plugin: The plugin has been compiled without support "
+ "for the \"CollectDelayAccounting\" option.");
+#endif
+ } else {
+ ERROR("processes plugin: Option \"%s\" not allowed here.", c->key);
}
} /* for (ci->children) */
} /* void ps_tune_instance */
cf_util_get_boolean(c, &report_fd_num);
} else if (strcasecmp(c->key, "CollectMemoryMaps") == 0) {
cf_util_get_boolean(c, &report_maps_num);
+ } else if (strcasecmp(c->key, "CollectDelayAccounting") == 0) {
+#if HAVE_LIBTASKSTATS
+ cf_util_get_boolean(c, &report_delay);
+#else
+ WARNING("processes plugin: The plugin has been compiled without support "
+ "for the \"CollectDelayAccounting\" option.");
+#endif
} else {
ERROR("processes plugin: The `%s' configuration option is not "
"understood and will be ignored.",
#elif KERNEL_LINUX
pagesize_g = sysconf(_SC_PAGESIZE);
DEBUG("pagesize_g = %li; CONFIG_HZ = %i;", pagesize_g, CONFIG_HZ);
+
+#if HAVE_LIBTASKSTATS
+ if (taskstats_handle == NULL) {
+ taskstats_handle = ts_create();
+ if (taskstats_handle == NULL) {
+ WARNING("processes plugin: Creating taskstats handle failed.");
+ }
+ }
+#endif
/* #endif KERNEL_LINUX */
#elif HAVE_LIBKVM_GETPROCS && \
plugin_dispatch_values(&vl);
}
+ /* The ps->delay_* metrics are in nanoseconds per second. Convert to seconds
+ * per second. */
+ gauge_t const delay_factor = 1000000000.0;
+
+ struct {
+ char *type_instance;
+ gauge_t rate_ns;
+ } delay_metrics[] = {
+ {"delay-cpu", ps->delay_cpu},
+ {"delay-blkio", ps->delay_blkio},
+ {"delay-swapin", ps->delay_swapin},
+ {"delay-freepages", ps->delay_freepages},
+ };
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(delay_metrics); i++) {
+ if (isnan(delay_metrics[i].rate_ns)) {
+ continue;
+ }
+ sstrncpy(vl.type, "delay_rate", sizeof(vl.type));
+ sstrncpy(vl.type_instance, delay_metrics[i].type_instance,
+ sizeof(vl.type_instance));
+ vl.values[0].gauge = delay_metrics[i].rate_ns * delay_factor;
+ vl.values_len = 1;
+ plugin_dispatch_values(&vl);
+ }
+
DEBUG(
"name = %s; num_proc = %lu; num_lwp = %lu; num_fd = %lu; num_maps = %lu; "
"vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
"io_rchar = %" PRIi64 "; io_wchar = %" PRIi64 "; "
"io_syscr = %" PRIi64 "; io_syscw = %" PRIi64 "; "
"io_diskr = %" PRIi64 "; io_diskw = %" PRIi64 "; "
- "cswitch_vol = %" PRIi64 "; cswitch_invol = %" PRIi64 ";",
+ "cswitch_vol = %" PRIi64 "; cswitch_invol = %" PRIi64 "; "
+ "delay_cpu = %g; delay_blkio = %g; "
+ "delay_swapin = %g; delay_freepages = %g;",
ps->name, ps->num_proc, ps->num_lwp, ps->num_fd, ps->num_maps,
ps->vmem_size, ps->vmem_rss, ps->vmem_data, ps->vmem_code,
ps->vmem_minflt_counter, ps->vmem_majflt_counter, ps->cpu_user_counter,
ps->cpu_system_counter, ps->io_rchar, ps->io_wchar, ps->io_syscr,
ps->io_syscw, ps->io_diskr, ps->io_diskw, ps->cswitch_vol,
- ps->cswitch_invol);
+ ps->cswitch_invol, ps->delay_cpu, ps->delay_blkio, ps->delay_swapin,
+ ps->delay_freepages);
} /* void ps_submit_proc_list */
return (count >= 1) ? count : 1;
} /* int ps_count_fd (pid) */
+#if HAVE_LIBTASKSTATS
+static int ps_delay(process_entry_t *ps) {
+ if (taskstats_handle == NULL) {
+ return ENOTCONN;
+ }
+
+ int status = ts_delay_by_tgid(taskstats_handle, (uint32_t)ps->id, &ps->delay);
+ if (status == EPERM) {
+ static c_complain_t c;
+#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_NET_ADMIN)
+ if (check_capability(CAP_NET_ADMIN) != 0) {
+ if (getuid() == 0) {
+ c_complain(
+ LOG_ERR, &c,
+ "processes plugin: Reading Delay Accounting metric failed: %s. "
+ "collectd is running as root, but missing the CAP_NET_ADMIN "
+ "capability. The most common cause for this is that the init "
+ "system is dropping capabilities.",
+ STRERROR(status));
+ } else {
+ c_complain(
+ LOG_ERR, &c,
+ "processes plugin: Reading Delay Accounting metric failed: %s. "
+ "collectd is not running as root and missing the CAP_NET_ADMIN "
+ "capability. Either run collectd as root or grant it the "
+ "CAP_NET_ADMIN capability using \"setcap cap_net_admin=ep " PREFIX
+ "/sbin/collectd\".",
+ STRERROR(status));
+ }
+ } else {
+ ERROR("processes plugin: ts_delay_by_tgid failed: %s. The CAP_NET_ADMIN "
+ "capability is available (I checked), so this error is utterly "
+ "unexpected.",
+ STRERROR(status));
+ }
+#else
+ c_complain(LOG_ERR, &c,
+ "processes plugin: Reading Delay Accounting metric failed: %s. "
+ "Reading Delay Accounting metrics requires root privileges.",
+ STRERROR(status));
+#endif
+ return status;
+ } else if (status != 0) {
+ ERROR("processes plugin: ts_delay_by_tgid failed: %s", STRERROR(status));
+ return status;
+ }
+
+ return 0;
+}
+#endif
+
static void ps_fill_details(const procstat_t *ps, process_entry_t *entry) {
if (entry->has_io == 0) {
ps_read_io(entry);
}
entry->has_fd = 1;
}
+
+#if HAVE_LIBTASKSTATS
+ if (ps->report_delay && !entry->has_delay) {
+ if (ps_delay(entry) == 0) {
+ entry->has_delay = 1;
+ }
+ }
+#endif
} /* void ps_fill_details (...) */
+/* ps_read_process reads process counters on Linux. */
static int ps_read_process(long pid, process_entry_t *ps, char *state) {
char filename[64];
char buffer[1024];
return 0;
}
#endif /* HAVE_THREAD_INFO */
-/* ------- end of additional functions for KERNEL_LINUX/HAVE_THREAD_INFO -------
- */
+/* end of additional functions for KERNEL_LINUX/HAVE_THREAD_INFO */
/* do actual readings from kernel */
static int ps_read(void) {
/* If not using logical core numbering, set core id */
if (!config_lcn) {
if (topology.num_packages > 1)
- snprintf(name, sizeof(name), "pkg%02d-core%02d", p->package_id, c->core_id);
+ snprintf(name, sizeof(name), "pkg%02d-core%02d", p->package_id,
+ c->core_id);
else
snprintf(name, sizeof(name), "core%02d", c->core_id);
}
case 0x45: /* HSW */
case 0x46: /* HSW */
case 0x3D: /* BDW */
+ case 0x5E: /* SKL */
do_rapl = RAPL_PKG | RAPL_CORES | RAPL_GFX;
break;
case 0x3F: /* HSX */
current_connections value:GAUGE:0:U
current_sessions value:GAUGE:0:U
delay value:GAUGE:-1000000:1000000
+delay_rate value:GAUGE:0:U
derive value:DERIVE:0:U
df used:GAUGE:0:1125899906842623, free:GAUGE:0:1125899906842623
df_complex value:GAUGE:0:U
sstrncpy(vl.type, data->type, sizeof(vl.type));
for (size_t i = 0; i < data->latency_config.percentile_num; i++) {
if (strlen(data->type_instance) != 0)
- snprintf(vl.type_instance, sizeof(vl.type_instance), "%.117s-%.2f",
+ snprintf(vl.type_instance, sizeof(vl.type_instance), "%.50s-%.5g",
data->type_instance, data->latency_config.percentile[i]);
else
- snprintf(vl.type_instance, sizeof(vl.type_instance), "%.0f",
+ snprintf(vl.type_instance, sizeof(vl.type_instance), "%.5g",
data->latency_config.percentile[i]);
vl.values = &(value_t){
bucket.upper_bound ? CDTIME_T_TO_DOUBLE(bucket.upper_bound) : INFINITY;
if (strlen(data->type_instance) != 0)
- snprintf(vl.type_instance, sizeof(vl.type_instance),
- "%.54s-%.54s-%.2g_%.2g", data->type, data->type_instance,
- lower_bound, upper_bound);
+ snprintf(vl.type_instance, sizeof(vl.type_instance), "%.50s-%.50s-%g_%g",
+ data->type, data->type_instance, lower_bound, upper_bound);
else
- snprintf(vl.type_instance, sizeof(vl.type_instance), "%.107s-%.2g_%.2g",
+ snprintf(vl.type_instance, sizeof(vl.type_instance), "%.50s-%g_%g",
data->type, lower_bound, upper_bound);
vl.values = &(value_t){
--- /dev/null
+/**
+ * collectd - src/utils_taskstats.c
+ * Copyright (C) 2017 Florian octo Forster
+ *
+ * ISC License (ISC)
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ */
+
+#include "collectd.h"
+#include "utils_taskstats.h"
+
+#include "common.h"
+#include "plugin.h"
+#include "utils_time.h"
+
+#include <libmnl/libmnl.h>
+#include <linux/genetlink.h>
+#include <linux/taskstats.h>
+
+struct ts_s {
+ struct mnl_socket *nl;
+ pid_t pid;
+ uint32_t seq;
+ uint16_t genl_id_taskstats;
+ unsigned int port_id;
+};
+
+/* nlmsg_errno returns the errno encoded in nlh or zero if not an error. */
+static int nlmsg_errno(struct nlmsghdr *nlh, size_t sz) {
+ if (!mnl_nlmsg_ok(nlh, (int)sz)) {
+ ERROR("utils_taskstats: mnl_nlmsg_ok failed.");
+ return EPROTO;
+ }
+
+ if (nlh->nlmsg_type != NLMSG_ERROR) {
+ return 0;
+ }
+
+ struct nlmsgerr *nlerr = mnl_nlmsg_get_payload(nlh);
+ /* (struct nlmsgerr).error holds a negative errno. */
+ return nlerr->error * (-1);
+}
+
+static int get_taskstats_attr_cb(const struct nlattr *attr, void *data) {
+ struct taskstats *ret_taskstats = data;
+
+ uint16_t type = mnl_attr_get_type(attr);
+ switch (type) {
+ case TASKSTATS_TYPE_STATS:
+ if (mnl_attr_get_payload_len(attr) != sizeof(*ret_taskstats)) {
+ ERROR("utils_taskstats: mnl_attr_get_payload_len(attr) = %" PRIu32
+ ", want %zu",
+ mnl_attr_get_payload_len(attr), sizeof(*ret_taskstats));
+ return MNL_CB_ERROR;
+ }
+ struct taskstats *ts = mnl_attr_get_payload(attr);
+ memmove(ret_taskstats, ts, sizeof(*ret_taskstats));
+ return MNL_CB_OK;
+
+ case TASKSTATS_TYPE_AGGR_PID: /* fall through */
+ case TASKSTATS_TYPE_AGGR_TGID:
+ return mnl_attr_parse_nested(attr, get_taskstats_attr_cb, ret_taskstats);
+
+ case TASKSTATS_TYPE_PID: /* fall through */
+ case TASKSTATS_TYPE_TGID:
+ /* ignore */
+ return MNL_CB_OK;
+
+ default:
+ DEBUG("utils_taskstats: unknown attribute %" PRIu16
+ ", want one of TASKSTATS_TYPE_AGGR_PID/TGID, TASKSTATS_TYPE_STATS",
+ type);
+ }
+ return MNL_CB_OK;
+}
+
+static int get_taskstats_msg_cb(const struct nlmsghdr *nlh, void *data) {
+ return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), get_taskstats_attr_cb,
+ data);
+}
+
+static int get_taskstats(ts_t *ts, uint32_t tgid,
+ struct taskstats *ret_taskstats) {
+ char buffer[MNL_SOCKET_BUFFER_SIZE];
+ uint32_t seq = ts->seq++;
+
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buffer);
+ *nlh = (struct nlmsghdr){
+ .nlmsg_len = nlh->nlmsg_len,
+ .nlmsg_type = ts->genl_id_taskstats,
+ .nlmsg_flags = NLM_F_REQUEST,
+ .nlmsg_seq = seq,
+ .nlmsg_pid = ts->pid,
+ };
+
+ struct genlmsghdr *genh = mnl_nlmsg_put_extra_header(nlh, sizeof(*genh));
+ *genh = (struct genlmsghdr){
+ .cmd = TASKSTATS_CMD_GET,
+ .version = TASKSTATS_GENL_VERSION, // or TASKSTATS_VERSION?
+ };
+
+ // mnl_attr_put_u32(nlh, TASKSTATS_CMD_ATTR_PID, tgid);
+ mnl_attr_put_u32(nlh, TASKSTATS_CMD_ATTR_TGID, tgid);
+
+ if (mnl_socket_sendto(ts->nl, nlh, nlh->nlmsg_len) < 0) {
+ int status = errno;
+ ERROR("utils_taskstats: mnl_socket_sendto() = %s", STRERROR(status));
+ return status;
+ }
+
+ int status = mnl_socket_recvfrom(ts->nl, buffer, sizeof(buffer));
+ if (status < 0) {
+ status = errno;
+ ERROR("utils_taskstats: mnl_socket_recvfrom() = %s", STRERROR(status));
+ return status;
+ } else if (status == 0) {
+ ERROR("utils_taskstats: mnl_socket_recvfrom() = 0");
+ return ECONNABORTED;
+ }
+ size_t buffer_size = (size_t)status;
+
+ if ((status = nlmsg_errno((void *)buffer, buffer_size)) != 0) {
+ ERROR("utils_taskstats: TASKSTATS_CMD_GET(TASKSTATS_CMD_ATTR_TGID = "
+ "%" PRIu32 ") = %s",
+ (uint32_t)tgid, STRERROR(status));
+ return status;
+ }
+
+ status = mnl_cb_run(buffer, buffer_size, seq, ts->port_id,
+ get_taskstats_msg_cb, ret_taskstats);
+ if (status < MNL_CB_STOP) {
+ ERROR("utils_taskstats: Parsing message failed.");
+ return EPROTO;
+ }
+
+ return 0;
+}
+
+static int get_family_id_attr_cb(const struct nlattr *attr, void *data) {
+ uint16_t type = mnl_attr_get_type(attr);
+ if (type != CTRL_ATTR_FAMILY_ID) {
+ return MNL_CB_OK;
+ }
+
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) {
+ ERROR("mnl_attr_validate() = %s", STRERRNO);
+ return MNL_CB_ERROR;
+ }
+
+ uint16_t *ret_family_id = data;
+ *ret_family_id = mnl_attr_get_u16(attr);
+ return MNL_CB_STOP;
+}
+
+static int get_family_id_msg_cb(const struct nlmsghdr *nlh, void *data) {
+ return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), get_family_id_attr_cb,
+ data);
+}
+
+/* get_family_id initializes ts->genl_id_taskstats. Returns 0 on success and
+ * an error code otherwise. */
+static int get_family_id(ts_t *ts) {
+ char buffer[MNL_SOCKET_BUFFER_SIZE];
+ uint32_t seq = ts->seq++;
+
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buffer);
+ *nlh = (struct nlmsghdr){
+ .nlmsg_len = nlh->nlmsg_len,
+ .nlmsg_type = GENL_ID_CTRL,
+ .nlmsg_flags = NLM_F_REQUEST,
+ .nlmsg_seq = seq,
+ .nlmsg_pid = ts->pid,
+ };
+
+ struct genlmsghdr *genh = mnl_nlmsg_put_extra_header(nlh, sizeof(*genh));
+ *genh = (struct genlmsghdr){
+ .cmd = CTRL_CMD_GETFAMILY, .version = 0x01,
+ };
+
+ mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, TASKSTATS_GENL_NAME);
+
+ assert(genh->cmd == CTRL_CMD_GETFAMILY);
+ assert(genh->version == TASKSTATS_GENL_VERSION);
+
+ if (mnl_socket_sendto(ts->nl, nlh, nlh->nlmsg_len) < 0) {
+ int status = errno;
+ ERROR("utils_taskstats: mnl_socket_sendto() = %s", STRERROR(status));
+ return status;
+ }
+
+ ts->genl_id_taskstats = 0;
+ while (42) {
+ int status = mnl_socket_recvfrom(ts->nl, buffer, sizeof(buffer));
+ if (status < 0) {
+ status = errno;
+ ERROR("utils_taskstats: mnl_socket_recvfrom() = %s", STRERROR(status));
+ return status;
+ } else if (status == 0) {
+ break;
+ }
+ size_t buffer_size = (size_t)status;
+
+ if ((status = nlmsg_errno((void *)buffer, buffer_size)) != 0) {
+ ERROR("utils_taskstats: CTRL_CMD_GETFAMILY(\"%s\"): %s",
+ TASKSTATS_GENL_NAME, STRERROR(status));
+ return status;
+ }
+
+ status = mnl_cb_run(buffer, buffer_size, seq, ts->port_id,
+ get_family_id_msg_cb, &ts->genl_id_taskstats);
+ if (status < MNL_CB_STOP) {
+ ERROR("utils_taskstats: Parsing message failed.");
+ return EPROTO;
+ } else if (status == MNL_CB_STOP) {
+ break;
+ }
+ }
+
+ if (ts->genl_id_taskstats == 0) {
+ ERROR("utils_taskstats: Netlink communication succeeded, but "
+ "genl_id_taskstats is still zero.");
+ return ENOENT;
+ }
+
+ return 0;
+}
+
+void ts_destroy(ts_t *ts) {
+ if (ts == NULL) {
+ return;
+ }
+
+ if (ts->nl != NULL) {
+ mnl_socket_close(ts->nl);
+ ts->nl = NULL;
+ }
+
+ sfree(ts);
+}
+
+ts_t *ts_create(void) {
+ ts_t *ts = calloc(1, sizeof(*ts));
+ if (ts == NULL) {
+ ERROR("utils_taskstats: calloc failed: %s", STRERRNO);
+ return NULL;
+ }
+
+ if ((ts->nl = mnl_socket_open(NETLINK_GENERIC)) == NULL) {
+ ERROR("utils_taskstats: mnl_socket_open(NETLINK_GENERIC) = %s", STRERRNO);
+ ts_destroy(ts);
+ return NULL;
+ }
+
+ if (mnl_socket_bind(ts->nl, 0, MNL_SOCKET_AUTOPID) != 0) {
+ ERROR("utils_taskstats: mnl_socket_bind() = %s", STRERRNO);
+ ts_destroy(ts);
+ return NULL;
+ }
+
+ ts->pid = getpid();
+ ts->port_id = mnl_socket_get_portid(ts->nl);
+
+ int status = get_family_id(ts);
+ if (status != 0) {
+ ERROR("utils_taskstats: get_family_id() = %s", STRERROR(status));
+ ts_destroy(ts);
+ return NULL;
+ }
+
+ return ts;
+}
+
+int ts_delay_by_tgid(ts_t *ts, uint32_t tgid, ts_delay_t *out) {
+ if ((ts == NULL) || (out == NULL)) {
+ return EINVAL;
+ }
+
+ struct taskstats raw = {0};
+
+ int status = get_taskstats(ts, tgid, &raw);
+ if (status != 0) {
+ return status;
+ }
+
+ *out = (ts_delay_t){
+ .cpu_ns = raw.cpu_delay_total,
+ .blkio_ns = raw.blkio_delay_total,
+ .swapin_ns = raw.swapin_delay_total,
+ .freepages_ns = raw.freepages_delay_total,
+ };
+ return 0;
+}
--- /dev/null
+/**
+ * collectd - src/utils_taskstats.h
+ * Copyright (C) 2017 Florian octo Forster
+ *
+ * ISC License (ISC)
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ */
+
+#ifndef UTILS_TASKSTATS_H
+#define UTILS_TASKSTATS_H 1
+
+#include "collectd.h"
+
+#include "utils_time.h"
+
+struct ts_s;
+typedef struct ts_s ts_t;
+
+typedef struct {
+ uint64_t cpu_ns;
+ uint64_t blkio_ns;
+ uint64_t swapin_ns;
+ uint64_t freepages_ns;
+} ts_delay_t;
+
+ts_t *ts_create(void);
+void ts_destroy(ts_t *);
+
+/* ts_delay_by_tgid returns Linux delay accounting information for the task
+ * identified by tgid. Returns zero on success and an errno otherwise. */
+int ts_delay_by_tgid(ts_t *ts, uint32_t tgid, ts_delay_t *out);
+
+#endif /* UTILS_TASKSTATS_H */