static void *_stp_shm_base;
static void *_stp_shm_alloc(size_t size);
+#include "session_attributes.h"
#include "transport.h"
// Global state shared throughout the module
unsigned long _hash_seed;
+ struct _stp_session_attributes _session_attributes;
+
#ifdef STP_ALIBI
atomic_t _probe_alibi[STP_PROBE_COUNT];
#endif
}
+static inline struct _stp_session_attributes *stp_session_attributes(void)
+{
+ if (likely(_stp_session()))
+ return &_stp_session()->_session_attributes;
+ return NULL;
+}
+
+
#ifdef STP_ALIBI
static inline atomic_t *probe_alibi(size_t index)
{
_stp_session()->_hash_seed = _stp_random_u ((unsigned long)-1);
+ stp_session_attributes_init();
+
#ifdef STP_ALIBI
// Initialize all the alibi counters
for (i = 0; i < STP_PROBE_COUNT; ++i)
/* see common_session_state.h */
static inline struct _stp_transport_session_data *stp_transport_data(void);
+static inline struct _stp_session_attributes *stp_session_attributes(void);
/*
* By definition, we can only debug our own processes with dyninst, so
#include "addr-map.c"
#include "stat.c"
#include "unwind.c"
+#include "session_attributes.c"
/* Support function for int64_t module parameters. */
static int set_int64_t(const char *val, int64_t *mp)
--- /dev/null
+// stapdyn session attribute code
+// Copyright (C) 2013 Red Hat Inc.
+//
+// This file is part of systemtap, and is free software. You can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License (GPL); either version 2, or (at your option) any
+// later version.
+
+#ifndef SESSION_ATTRIBUTES_C
+#define SESSION_ATTRIBUTES_C
+
+#include "session_attributes.h"
+
+static struct _stp_session_attributes _stp_init_session_attributes = {
+ .log_level = 0,
+ .suppress_warnings = 0,
+};
+
+static void stp_session_attributes_init(void)
+{
+ stp_session_attributes()->log_level
+ = _stp_init_session_attributes.log_level;
+ stp_session_attributes()->suppress_warnings
+ = _stp_init_session_attributes.suppress_warnings;
+}
+
+static int stp_session_attribute_setter(const char *name, const char *value)
+{
+ // Note that We start all internal variables with '@', since
+ // that can't start a "real" variable.
+ if (strcmp(name, "@log_level") == 0) {
+ _stp_init_session_attributes.log_level = strtoul(value, NULL,
+ 10);
+ return 0;
+ }
+ else if (strcmp(name, "@suppress_warnings") == 0) {
+ _stp_init_session_attributes.suppress_warnings
+ = strtoul(value, NULL, 10);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+#endif // SESSION_ATTRIBUTES_C
--- /dev/null
+// stapdyn session attribute declarations
+// Copyright (C) 2013 Red Hat Inc.
+//
+// This file is part of systemtap, and is free software. You can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License (GPL); either version 2, or (at your option) any
+// later version.
+
+#ifndef SESSION_ATTRIBUTES_H
+#define SESSION_ATTRIBUTES_H
+
+struct _stp_session_attributes {
+ unsigned long log_level;
+ unsigned long suppress_warnings;
+};
+
+static void stp_session_attributes_init(void);
+static int stp_session_attribute_setter(const char *name, const char *value);
+
+#endif // SESSION_ATTRIBUTES_H
/**** STAP 2.2 : ****/
/* The following function is dynamically generated by systemtap, and
- used by stapdyn to modify global variables at module startup only
- (that is, *before* running stp_dyninst_session_init()): */
+ * used by stapdyn to modify global variables at module startup only
+ * (that is, *before* running stp_dyninst_session_init()). If the
+ * name starts with '@', the name is assumed to be an internal value. */
extern int stp_global_setter(const char *name, const char *value);
#include <errno.h>
#include <string.h>
+#include <search.h>
#include "transport.h"
pthread_mutex_unlock(&(sess_data->queue_mutex));
}
+/* Handle duplicate warning elimination. Returns 0 if we've seen this
+ * warning (and should be eliminated), 1 otherwise. */
+static int
+__stp_d_t_eliminate_duplicate_warnings(char *data, size_t bytes)
+{
+ static void *seen = 0;
+ static unsigned seen_count = 0;
+ char *dupstr = strndup (data, bytes);
+ char *retval;
+ int rc = 1;
+
+ if (! dupstr) {
+ /* OOM, should not happen. */
+ return 1;
+ }
+
+ retval = tfind (dupstr, &seen,
+ (int (*)(const void*, const void*))strcmp);
+ if (! retval) { /* new message */
+ /* We set a maximum for stored warning messages, to
+ * prevent a misbehaving script/environment from
+ * emitting countless _stp_warn()s, and overflow
+ * staprun's memory. */
+#define MAX_STORED_WARNINGS 1024
+ if (seen_count++ == MAX_STORED_WARNINGS) {
+ fprintf(_stp_err,
+ "WARNING deduplication table full\n");
+ free (dupstr);
+ }
+ else if (seen_count > MAX_STORED_WARNINGS) {
+ /* Be quiet in the future, but stop counting
+ * to preclude overflow. */
+ free (dupstr);
+ seen_count = MAX_STORED_WARNINGS + 1;
+ }
+ else if (seen_count < MAX_STORED_WARNINGS) {
+ /* NB: don't free dupstr; it's going into the tree. */
+ retval = tsearch (dupstr, & seen,
+ (int (*)(const void*, const void*))strcmp);
+ if (retval == 0) {
+ /* OOM, should not happen. Next time
+ * we should get the 'full'
+ * message. */
+ free (dupstr);
+ seen_count = MAX_STORED_WARNINGS;
+ }
+ }
+ }
+ else { /* old message */
+ free (dupstr);
+ rc = 0;
+ }
+ return rc;
+}
+
static void *
_stp_dyninst_transport_thread_func(void *arg __attribute((unused)))
{
// Process the queue twice. First handle the OOB data.
for (size_t i = 0; i < q->items; i++) {
+ int write_data = 1;
item = &(q->queue[i]);
if (item->type != STP_DYN_OOB_DATA)
continue;
c = stp_session_context(item->data_index);
data = &c->transport_data;
read_ptr = data->log_buf + item->offset;
- size = write(err_fd, read_ptr, item->bytes);
- if (size != item->bytes)
- fprintf(_stp_err,
- "write only wrote %ld bytes (%ld),"
- " errno %d\n",
- (long)size, (long)item->bytes, errno);
- data->log_start = _STP_D_T_LOG_INC(data->log_start);
+
+ /* Note that "WARNING:" should not be
+ * translated, since it is part of the module
+ * cmd protocol. */
+ if (strncmp(read_ptr, "WARNING:", 7) == 0) {
+ if (stp_session_attributes()->suppress_warnings) {
+ write_data = 0;
+ }
+ /* If we're not verbose, eliminate
+ * duplicate warning messages. */
+ else if (stp_session_attributes()->log_level
+ == 0) {
+ write_data = __stp_d_t_eliminate_duplicate_warnings(read_ptr, item->bytes);
+ }
+ }
+
+ if (write_data) {
+ size = write(err_fd, read_ptr, item->bytes);
+ if (size != item->bytes)
+ fprintf(_stp_err,
+ "write only wrote %ld bytes"
+ " (%ld), errno %d\n",
+ (long)size,
+ (long)item->bytes, errno);
+ }
// Signal there is a log buffer available to
// any waiters.
+ data->log_start = _STP_D_T_LOG_INC(data->log_start);
pthread_mutex_lock(&(data->log_mutex));
pthread_cond_signal(&(data->log_space_avail));
pthread_mutex_unlock(&(data->log_mutex));
unsigned stapdyn_log_level = 0;
// Whether to suppress warnings, set by -w
-bool stapdyn_supress_warnings = false;
+bool stapdyn_suppress_warnings = false;
// Return a stream for logging at the given verbosity level.
ostream&
ostream&
stapwarn(void)
{
- if (stapdyn_supress_warnings)
+ if (stapdyn_suppress_warnings)
return nullstream;
return clog << program_invocation_short_name << ": WARNING: ";
}
extern unsigned stapdyn_log_level;
// Whether to suppress warnings, set by -w
-extern bool stapdyn_supress_warnings;
+extern bool stapdyn_suppress_warnings;
// Return a stream for logging at the given verbosity level.
std::ostream& staplog(unsigned level=0);
return true;
}
+void
+mutator::init_session_attributes()
+{
+ typeof(&stp_global_setter) global_setter = NULL;
+ set_dlsym(global_setter, module, "stp_global_setter", false);
+
+ if (global_setter == NULL)
+ {
+ // Just return.
+ return;
+ }
+
+ // Note that the list of supported attributes should match with the
+ // list in 'struct _stp_sesion_attributes' in
+ // runtime/dyninst/session_attributes.h.
+
+ int rc = global_setter("@log_level", lex_cast(stapdyn_log_level).c_str());
+ if (rc != 0)
+ stapwarn() << "couldn't set 'log_level' global" << endl;
+
+ rc = global_setter("@suppress_warnings",
+ lex_cast(stapdyn_suppress_warnings).c_str());
+ if (rc != 0)
+ stapwarn() << "couldn't set 'suppress_warnings' global" << endl;
+
+ return;
+}
+
// Initialize the module session
bool
mutator::run_module_init()
if (!modoptions.empty() && !init_modoptions())
return false;
+ init_session_attributes();
+
int rc = session_init();
if (rc)
{
// Initialize the module global variables
bool init_modoptions();
+ // Initialize the session attributes
+ void init_session_attributes();
+
// Initialize the module session
bool run_module_init();
break;
case 'w':
- stapdyn_supress_warnings = true;
+ stapdyn_suppress_warnings = true;
break;
case 'V':
if {! [installtest_p]} { return }
-proc dyninst_kfails {index} {
- # With no real transport layer, dyninst can't eliminate duplicate
- # warnings. PR15110
- setup_kfail 15110 "*-*-*"
-}
-
foreach runtime [get_runtime_list] {
set test "warnings2 default"
if {$runtime != ""} {
lappend test "($runtime)"
- if { [info procs ${runtime}_kfails] ne "" } {
- ${runtime}_kfails $i
- }
spawn stap --runtime=$runtime \
-e {probe begin {warn ("1") warn ("2") warn ("1") exit ()}}
} else {
o->newline(-1) << "} else ";
}
- o->line() << "return -EINVAL;";
+
+ // Call the runtime function that handles session attributes, like
+ // log_level, etc.
+ o->line() << "return stp_session_attribute_setter(name, value);";
o->newline(-1) << "}";
o->newline();
}