diff options
Diffstat (limited to 'src/libpcp_fault')
-rw-r--r-- | src/libpcp_fault/README | 58 | ||||
-rw-r--r-- | src/libpcp_fault/pmfault.3 | 353 | ||||
-rw-r--r-- | src/libpcp_fault/src/GNUmakefile | 160 | ||||
-rwxr-xr-x | src/libpcp_fault/src/mk.exports | 14 |
4 files changed, 585 insertions, 0 deletions
diff --git a/src/libpcp_fault/README b/src/libpcp_fault/README new file mode 100644 index 0000000..b6f3f18 --- /dev/null +++ b/src/libpcp_fault/README @@ -0,0 +1,58 @@ +libpcp_fault is a version of libpcp that has fault injection +capabilities added and lock debug instrumentation. + +This library is NOT intended to be packaged or shipped, +rather it is intended for use with the PCP QA suite. + +Normal builds do not descend into this directory. + +To build and use the library, cd into here, then + + $ cd src + $ make + $ sudo make install + $ make clean + +To use the library in a test application, + + #include <pcp/fault.h> + + ... + +Then compile the code with -DPM_FAULT_INJECTION=1 and link with +-lpcp_fault instead of -lpcp + +To use with an executable that is already linked, set $LD_PRELOAD +in the environment to the full pathname of the installed +libpcp_fault, e.g. + + $ LD_PRELOAD=/usr/lib/libpcp_fault.so pminfo ... + +The mechanisms for defining fault injection points and controlling +the triggering of fault injection is described in the brief "man" +page found in the this directory. Review with + + $ man ./pmfault.3 + +Lock debugging is activated with -D options on the command line for +standard PCP applications: + + -D lock enables lock debug tracing, by default all lock and + unlock requests are traced with the source:lineno of + the caller and some symbolic reference to the lock + type ... lock recursion is reported by a [count=N] + note if the lock count is not 0 for a lock() and 1 + for an unlock() + + -D lock,appl0 + restrict the tracing to the global libpcp lock + + -D lock,appl1 + restrict tracing to the per-context locks + + -D lock,appl2 + restrict tracing to the ipc channel lock for each + context + +One or more appl? flags may be specified. -D lock is equivalent to +-D lock,appl0,appl1,appl2 diff --git a/src/libpcp_fault/pmfault.3 b/src/libpcp_fault/pmfault.3 new file mode 100644 index 0000000..5eef089 --- /dev/null +++ b/src/libpcp_fault/pmfault.3 @@ -0,0 +1,353 @@ +'\"macro stdmacro +.\" +.\" Copyright (c) 2011 Ken McDonell. All Rights Reserved. +.\" +.\" 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 2 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. +.\" +.\" +.TH PMFAULT 3 "" "Performance Co-Pilot" +.SH NAME +\f3__pmFaultInject\f1, +\f3PM_FAULT_POINT\f1, +\f3PM_FAULT_CHECK\f1, +\f3__pmFaultSummary\f1 \- Fault Injection Infrastracture for QA +.SH "C SYNOPSIS" +.ft 3 +#include <pcp/pmapi.h> +.br +#include <pcp/impl.h> +.br +#include <pcp/fault.h> +.sp +void __pmFaultInject(const char *\fIident\fP, int \fIclass\fP); +.br +void __pmFaultSummary(FILE *\fIf\fP); +.sp +PM_FAULT_POINT(\fIident\fP, \fIclass\fP); +.br +PM_FAULT_CHECK(\fIclass\fP); +.sp +cc \-DPM_FAULT_INJECTION=1 ... \-lpcp_fault +.ft 1 +.SH DESCRIPTION +.PP +As part of the coverage-driven changes to QA in PCP 3.6, it became +apparent that we needed someway to exercise the ``uncommon'' +code paths associated with error detection and recovery. +.PP +The facilities described below provide +a basic fault injection infrastructure (for +.I libpcp +only at this stage, alhough the mechanism is far more general and could +easily be extended). +.PP +A special build is required to create +.I libpcp_fault +and the associated +.I <pcp/fault.h> +header file. +Once this has been done, new QA applications may be built with +.B \-DPM_FAULT_INJECTION=1 +and/or existing applications can be exercised in presence of +fault injection by forcing +.I libpcp_fault +to be used in preference to +.I libpcp +as described below. +.PP +In the code to be tested, +.B __pmFaultInject +defines a fault point at which a fault of type +.I class +may be injected. +.I ident +is a string to uniquely identify the fault point across all +of the PCP source code, so something +like "libpcp/" __FILE__ ":<number>" works just fine. +The +.I ident +string also determines if a fault will be injected at run-time or not +\- refer to the +.B "RUN-TIME CONTROL" +section below. +.I class +selects a failure type, using one of the following defined +values (this list may well grow over time): +.TP +.B PM_FAULT_ALLOC +Will cause the +.B next +call to +.BR malloc (3), +.BR realloc (3) +or +.BR strdup (3) +to fail, returning NULL and setting +.I errno +to +.BR ENOMEM . +We could extend the coverage to all of the malloc-related routines, +but these three are sufficient to cover the vast majority of the uses +within +.IR libpcp . +.TP +.B PM_FAULT_PMAPI +Will cause the +.B next +call to a PMAPI routine +to fail by returning the (new) PCP error code +.BR PM_ERR_FAULT . +At the +this stage, only +.BR __pmRegisterAnon (3) +has been instrumented as a proof of concept for this part of the +facility. +.PP +To allow fault injection to co-exist within the production source +code, +.B PM_FAULT_POINT +is a macro that emits no code by default, but when +.B PM_FAULT_INJECTION +is defined this becomes a call to +.BR __pmFaultInject . +Throughout +.I libpcp +we use +.B PM_FAULT_POINT +and +.B not +.B __pmFaultInject +so that both +.I libpcp +and +.I libpcp_fault +can be built from the same source code. +.PP +Similarly, the macro +.B PM_FAULT_CHECK +emits no code unless +.B PM_FAULT_INJECTION +is defined, in which case if a fault of type +.I class +has been armed with +.B __pmFaultInject +then, the enclosing +routine will trigger the associated error behaviour. +For the moment, this only works for the +.I class +of +.B PM_FAULT_PMAPI +where the enclosing routine will return immediately with the value +.B PM_ERR_FAULT +\- this assumes the enclosing routine is of type +.B "int foo(...)" +like all of the PMAPI routines. +.PP +A summary of fault points seen and faults injected is produced +on stdio stream +.I f +by +.BR __pmFaultSummary . +.PP +Additional tracing (via +.B \-Dfault +and +.BR DBG_TRACE_FAULT ) +and a new +PMAPI error code (\c +.BR PM_ERR_FAULT ) +are also defined, although +these will only ever be seen or used in +.IR libpcp_fault . +If +.B DBG_TRACE_FAULT +is set the first time +.B __pmFaultInject +is called, then +.B __pmFaultSummary +will be called automatically to report on +.I stderr +when the application exits (via +.BR atexit (3)). +.PP +Fault injection cannot be nested. Each call to +.B __pmFaultInject +clears any previous fault injection that has been armed, but not yet +executed. +.PP +The fault injection infrastructure is +.B not +thread-safe and should only be used with applications that are +known to be single-threaded. + +.SH RUN-TIME CONTROL +.PP +By default, no fault injection is enabled at run-time, even when +.B __pmFaultInject +is called. +.PP +Faults are selectively enabled using a control file, identified by the environment +variable +.BR $PM_FAULT_CONTROL ; +if this is not set, no faults are enabled. +.PP +The control file (if it exists) is read the first time +.B __pmFaultInject +is called, and +contains lines of the form: +.ti +8n +.I ident +.I op +.I number +.br +that define fault injection guards. +.PP +.I ident +is a fault point string (as defined by a call to +.BR __pmFaultInject , +or more usually the +.B PM_FAULT_POINT +macro). So one needs access to the +.I libpcp +source code to determine the available +.I ident +strings and their semantics. +.PP +.I op +is one of the C-style operators +.BR >= , +.BR > , +.BR == , +.BR < , +.BR <= , +.B != +or +.BR % +and +.I number +is an unsigned integer. +.I op +.I number +is optional and the default is +.BR ">0" +.PP +The semantics of the fault injection guards are that each time +.B __pmFaultInject +is called for a particular +.IR ident , +a trip count is incremented (the first +trip is 1); if the C-style expression +.I tripcount +.I op +.I number +has the +value 1 (so +.B true +for most +.IR op s, +or the remainder equals 1 for the +.B % +.IR op ), +then +a fault of the +.I class +defined for the fault point associated with +.I ident +will be armed, and executed as soon as possible. +.PP +Within the control file, blank lines are ignored and lines +beginning with # are treated as comments. +.PP +For an existing application linked with +.I libpcp +fault injection may still be used by forcing +.I libpcp_fault +to be used in the place of +.IR libpcp . +The following example shows how this might be done. +.ft CW +.nf +$ export PM_FAULT_CONTROL=/tmp/control +$ cat $PM_FAULT_CONTROL +# ok for 2 trips, then inject errors +libpcp/events.c:1 >2 + +$ export LD_PRELOAD=/usr/lib/libpcp_fault.so +$ pmevent -Dfault -s 3 sample.event.records +host: localhost +samples: 3 +interval: 1.00 sec +sample.event.records[fungus]: 0 event records +__pmFaultInject(libpcp/events.c:1) ntrip=1 SKIP +sample.event.records[bogus]: 2 event records + 10:46:12.413 --- event record [0] flags 0x1 (point) --- + sample.event.param_string "fetch #0" + 10:46:12.413 --- event record [1] flags 0x1 (point) --- + sample.event.param_string "bingo!" +__pmFaultInject(libpcp/events.c:1) ntrip=2 SKIP +sample.event.records[fungus]: 1 event records + 10:46:03.416 --- event record [0] flags 0x1 (point) --- +__pmFaultInject(libpcp/events.c:1) ntrip=3 INJECT +sample.event.records[bogus]: pmUnpackEventRecords: Cannot allocate memory +__pmFaultInject(libpcp/events.c:1) ntrip=4 INJECT +sample.event.records[fungus]: pmUnpackEventRecords: Cannot allocate memory +__pmFaultInject(libpcp/events.c:1) ntrip=5 INJECT +sample.event.records[bogus]: pmUnpackEventRecords: Cannot allocate memory +=== Fault Injection Summary Report === +libpcp/events.c:1: guard trip>2, 5 trips, 3 faults +.fi +.ft + +.SH EXAMPLES +Refer to the PCP and PCP QA source code. +.PP +.I src/libpcp/src/derive.c +uses +.BR PM_FAULT_CHECK . +.PP +.I src/libpcp/src/err.c +and +.I src/libpcp/src/events.c +use +.BR PM_FAULT_POINT . +.PP +.I src/libpcp/src/fault.c +contains all of the the underlying implementation. +.PP +.I src/libpcp_fault +contains the recipe and Makefile for creating and +installing +.I libpcp_fault +and +.IR <pcp/fault.h> . +.PP +.I QA/477 +and +.I QA/478 +show examples of control file use. + +.SH ENVIRONMENT +.TP +.B PM_FAULT_CONTROL +Full path to the fault injection control file. +.TP +.B LD_PRELOAD +Force +.I libpcp_fault +to be used in preference to +.IR libpcp . + +.SH SEE ALSO +.BR PMAPI (3) +.SH DIAGNOSTICS +.PP +Some non-recoverable errors are reported on +.IR stderr . diff --git a/src/libpcp_fault/src/GNUmakefile b/src/libpcp_fault/src/GNUmakefile new file mode 100644 index 0000000..0876060 --- /dev/null +++ b/src/libpcp_fault/src/GNUmakefile @@ -0,0 +1,160 @@ +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# Copyright (c) 2011 Ken McDonell. All Rights Reserved. +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# This library 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 Lesser General Public +# License for more details. +# + +LCFLAGS += -DPM_FAULT_INJECTION=1 -DPM_MULTI_THREAD_DEBUG=1 + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs +-include ./GNUlocaldefs + +CFILES = connect.c context.c desc.c err.c fetch.c freeresult.c \ + help.c instance.c p_desc.c p_error.c p_fetch.c p_instance.c \ + p_profile.c p_result.c p_text.c p_pmns.c p_creds.c p_auth.c \ + pdu.c pdubuf.c pmns.c profile.c store.c units.c util.c ipc.c \ + sortinst.c logmeta.c logportmap.c logutil.c tz.c interp.c \ + checksum.c rtime.c tv.c spec.c fetchlocal.c optfetch.c AF.c \ + stuffvalue.c endian.c config.c auxconnect.c auxserver.c discovery.c \ + p_lcontrol.c p_lrequest.c p_lstatus.c logconnect.c logcontrol.c \ + connectlocal.c derive.c derive_fetch.c events.c lock.c hash.c \ + fault.c access.c probe.c +HFILES = derive.h internal.h avahi.h probe.h +VERSION_SCRIPT = exports + +LSRCFILES = check-statics $(VERSION_SCRIPT) + +LLDLIBS += $(LIB_FOR_MATH) $(LIB_FOR_PTHREADS) -lpcp_pmda + +ifeq "$(ENABLE_SECURE)" "true" +LLDLIBS += $(LIB_FOR_SSL) $(LIB_FOR_NSS) $(LIB_FOR_NSPR) $(LIB_FOR_SASL) +LCFLAGS += $(NSSCFLAGS) $(NSPRCFLAGS) $(SASLCFLAGS) +CFILES += secureserver.c secureconnect.c +else +LSRCFILES += secureserver.c secureconnect.c +endif + +ifeq "$(ENABLE_AVAHI)" "true" +LLDLIBS += $(LIB_FOR_AVAHI) +LCFLAGS += $(AVAHICFLAGS) +CFILES += avahi.c +else +LSRCFILES += avahi.c +endif + +ifneq "$(TARGET_OS)" "mingw" +CFILES += accounts.c +LSRCFILES += win32.c +else +CFILES += win32.c +LSRCFILES += accounts.c +LLDLIBS += -lpsapi +endif + +ifeq "$(TARGET_OS)" "solaris" +# enables standards compliant thread-safe interfaces (accounts.c) +LCFLAGS += -D_POSIX_PTHREAD_SEMANTICS +endif + +ifeq "$(LIB_FOR_BASENAME)" "-lpcp" +# don't need to be linked to myself in this case! +LIB_FOR_BASENAME = +endif + +LLDLIBS += $(LIB_FOR_MATH) $(LIB_FOR_PTHREADS) + +LCFLAGS += -DLIBPCP_INTERNAL '-DEXEC_SUFFIX="$(EXECSUFFIX)"' \ + '-DDSO_SUFFIX="$(DSOSUFFIX)"' + +DSOVERSION = 3 +STATICLIBTARGET = libpcp_fault.a +LIBTARGET = libpcp_fault.$(DSOSUFFIX).$(DSOVERSION) +SYMTARGET = libpcp_fault.$(DSOSUFFIX) libpcp_fault.$(DSOSUFFIX).2 + +ifeq "$(PACKAGE_DISTRIBUTION)" "debian" +SYMTARGET = libpcp_fault.$(DSOSUFFIX) +endif +ifeq "$(TARGET_OS)" "darwin" +LIBTARGET = libpcp_fault.$(DSOVERSION).$(DSOSUFFIX) +SYMTARGET = libpcp_fault.$(DSOSUFFIX) +endif +ifeq "$(TARGET_OS)" "mingw" +STATICLIBTARGET = +LIBTARGET = libpcp_fault.$(DSOSUFFIX) +SYMTARGET = +endif +ifeq "$(ENABLE_SHARED)" "no" +LIBTARGET = +SYMTARGET = +endif + +LDIRT += $(SYMTARGET) $(LIBTARGET) $(STATICLIBTARGET) $(VERSION_SCRIPT) $(HFILES) $(CFILES) + +base default : $(HFILES) $(CFILES) $(VERSION_SCRIPT) $(LIBTARGET) $(SYMTARGET) $(STATICLIBTARGET) + +libpcp.so: $(SYMTARGET) + ln -s $(SYMTARGET) libpcp.so + +ifneq "$(SYMTARGET)" "" +$(SYMTARGET): + $(LN_S) -f $(LIBTARGET) $@ +endif + +include $(BUILDRULES) + +*.o: internal.h +derive.o derive_fetch.o: derive.h +util.o: $(TOPDIR)/src/include/pcp/pmdbg.h +fault.o: $(TOPDIR)/src/include/pcp/fault.h + +$(OBJECTS): $(TOPDIR)/src/include/pcp/pmapi.h \ + $(TOPDIR)/src/include/pcp/impl.h \ + $(TOPDIR)/src/include/pcp/platform_defs.h + +ifeq "$(TARGET_OS)" "mingw" +kernel_pmda_dso = windows +else +kernel_pmda_dso = $(TARGET_OS) +endif + +install : default +ifneq ($(LIBTARGET),) + $(INSTALL) -m 755 $(LIBTARGET) $(PCP_LIB_DIR)/$(LIBTARGET) +endif +ifneq ($(SYMTARGET),) + for tt in $(SYMTARGET); do \ + $(INSTALL) -S $(LIBTARGET) $(PCP_LIB_DIR)/$$tt || exit 1; \ + done +endif +ifneq ($(STATICLIBTARGET),) + $(INSTALL) -m 755 $(STATICLIBTARGET) $(PCP_LIB_DIR)/$(STATICLIBTARGET) +endif + $(INSTALL) -m 644 $(TOPDIR)/src/include/pcp/fault.h $(PCP_INC_DIR)/fault.h + +$(HFILES) $(CFILES): + ln -s ../../libpcp/src/$@ . + +$(VERSION_SCRIPT): ../../libpcp/src/$(VERSION_SCRIPT) mk.exports + ./mk.exports + +default_pcp : default + +install_pcp : install + +$(TOPDIR)/src/pmns/stdpmid: + cd $(@D); $(MAKE) $(@F) + +ifneq ($(LIBTARGET),) +$(LIBTARGET): $(VERSION_SCRIPT) +endif diff --git a/src/libpcp_fault/src/mk.exports b/src/libpcp_fault/src/mk.exports new file mode 100755 index 0000000..d732539 --- /dev/null +++ b/src/libpcp_fault/src/mk.exports @@ -0,0 +1,14 @@ +#!/bin/sh +# +# Make exports for libpcp_fault from the exports file for libpcp +# + +rm -f exports +sed <../../libpcp/src/exports >exports \ + -e '/ local: \*;/i\ +/* added for libpcp_fault */\ + __pmFault_malloc;\ + __pmFault_realloc;\ + __pmFault_strdup;\ + +' |