summaryrefslogtreecommitdiff
path: root/src/libpcp_trace
diff options
context:
space:
mode:
Diffstat (limited to 'src/libpcp_trace')
-rw-r--r--src/libpcp_trace/GNUmakefile28
-rw-r--r--src/libpcp_trace/src/GNUmakefile74
-rw-r--r--src/libpcp_trace/src/exports42
-rw-r--r--src/libpcp_trace/src/ftrace.c134
-rw-r--r--src/libpcp_trace/src/hash.c181
-rw-r--r--src/libpcp_trace/src/hash.h54
-rw-r--r--src/libpcp_trace/src/p_ack.c71
-rw-r--r--src/libpcp_trace/src/p_data.c179
-rw-r--r--src/libpcp_trace/src/pdu.c419
-rw-r--r--src/libpcp_trace/src/pdubuf.c182
-rw-r--r--src/libpcp_trace/src/trace.c950
11 files changed, 2314 insertions, 0 deletions
diff --git a/src/libpcp_trace/GNUmakefile b/src/libpcp_trace/GNUmakefile
new file mode 100644
index 0000000..477919d
--- /dev/null
+++ b/src/libpcp_trace/GNUmakefile
@@ -0,0 +1,28 @@
+#!gmake
+#
+# Copyright (c) 2000,2004 Silicon Graphics, Inc. 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.
+#
+
+TOPDIR = ../..
+include $(TOPDIR)/src/include/builddefs
+
+SUBDIRS = src
+
+default install : $(SUBDIRS)
+ $(SUBDIRS_MAKERULE)
+
+include $(BUILDRULES)
+
+default_pcp : default
+
+install_pcp : install
diff --git a/src/libpcp_trace/src/GNUmakefile b/src/libpcp_trace/src/GNUmakefile
new file mode 100644
index 0000000..a99c493
--- /dev/null
+++ b/src/libpcp_trace/src/GNUmakefile
@@ -0,0 +1,74 @@
+#
+# Copyright (c) 2013 Red Hat.
+# Copyright (c) 2000,2004 Silicon Graphics, Inc. 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.
+#
+
+TOPDIR = ../../..
+include $(TOPDIR)/src/include/builddefs
+
+HFILES = hash.h
+CFILES = trace.c hash.c pdu.c pdubuf.c p_ack.c p_data.c ftrace.c
+VERSION_SCRIPT = exports
+
+LCFLAGS = -DPMTRACE_DEBUG
+LLDLIBS = $(LIB_FOR_PTHREADS) -lpcp
+LSRCFILES = $(VERSION_SCRIPT)
+
+STATICLIBTARGET = libpcp_trace.a
+
+DSOVERSION = 2
+LIBTARGET = libpcp_trace.$(DSOSUFFIX).$(DSOVERSION)
+SYMTARGET = libpcp_trace.$(DSOSUFFIX)
+
+ifeq "$(TARGET_OS)" "darwin"
+LIBTARGET = libpcp_trace.$(DSOVERSION).$(DSOSUFFIX)
+endif
+ifeq "$(TARGET_OS)" "mingw"
+STATICLIBTARGET =
+LIBTARGET = libpcp_trace.$(DSOSUFFIX)
+SYMTARGET =
+endif
+ifeq "$(ENABLE_SHARED)" "no"
+LIBTARGET =
+SYMTARGET =
+endif
+
+LDIRT = $(SYMTARGET)
+
+base default: $(LIBTARGET) $(SYMTARGET) $(STATICLIBTARGET)
+
+ifneq ($(SYMTARGET),)
+$(SYMTARGET):
+ $(LN_S) -f $(LIBTARGET) $(SYMTARGET)
+endif
+
+include $(BUILDRULES)
+
+install : default
+ifneq ($(LIBTARGET),)
+ $(INSTALL) -m 755 $(LIBTARGET) $(PCP_LIB_DIR)/$(LIBTARGET)
+endif
+ifneq ($(SYMTARGET),)
+ $(INSTALL) -S $(LIBTARGET) $(PCP_LIB_DIR)/$(SYMTARGET)
+endif
+ifneq ($(STATICLIBTARGET),)
+ $(INSTALL) -m 755 $(STATICLIBTARGET) $(PCP_LIB_DIR)/$(STATICLIBTARGET)
+endif
+
+default_pcp : default
+
+install_pcp : install
+
+ifneq ($(LIBTARGET),)
+$(LIBTARGET): $(VERSION_SCRIPT)
+endif
diff --git a/src/libpcp_trace/src/exports b/src/libpcp_trace/src/exports
new file mode 100644
index 0000000..415e3c8
--- /dev/null
+++ b/src/libpcp_trace/src/exports
@@ -0,0 +1,42 @@
+PCP_TRACE_2.0 {
+ global:
+ /* C and Fortran calling interfaces */
+ pmtraceabort;
+ pmtraceabort_;
+ pmtracebegin;
+ pmtracebegin_;
+ pmtracecounter;
+ pmtracecounter_;
+ pmtraceend;
+ pmtraceend_;
+ pmtraceobs;
+ pmtraceobs_;
+ pmtracepoint;
+ pmtracepoint_;
+ pmtracestate;
+ pmtracestate_;
+ pmtraceerrstr;
+ pmtraceerrstr_;
+
+ /* Internals shared between pmdatrace and libpcp_trace */
+ __pmhashinit;
+ __pmhashinsert;
+ __pmhashlookup;
+ __pmhashtraverse;
+ __pmhashtrunc;
+ __pmstate;
+ __pmtracedecodeack;
+ __pmtracedecodedata;
+ __pmtracefindPDUbuf;
+ __pmtracegetPDU;
+ __pmtracemoreinput;
+ __pmtracenomoreinput;
+ __pmtracepinPDUbuf;
+ __pmtraceprotocol;
+ __pmtracesendack;
+ __pmtracesenddata;
+ __pmtraceunpinPDUbuf;
+ __pmtracexmitPDU;
+
+ local: *;
+};
diff --git a/src/libpcp_trace/src/ftrace.c b/src/libpcp_trace/src/ftrace.c
new file mode 100644
index 0000000..1dc8560
--- /dev/null
+++ b/src/libpcp_trace/src/ftrace.c
@@ -0,0 +1,134 @@
+/*
+ * ftrace.c - Fortran front-end to the libpcp_trace entry points
+ *
+ * Copyright (c) 1997-2000 Silicon Graphics, Inc. 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.
+ */
+
+#include "pmapi.h"
+#include "trace.h"
+#include "trace_dev.h"
+
+int
+pmtracebegin_(const char *tag, int tag_len)
+{
+ char *tmp = NULL;
+ int sts;
+
+ if ((tmp = malloc(tag_len + 1)) == NULL)
+ return -oserror();
+ strncpy(tmp, tag, tag_len);
+ tmp[tag_len] = '\0';
+ sts = pmtracebegin(tmp);
+ free(tmp);
+ return sts;
+}
+
+int
+pmtraceend_(const char *tag, int tag_len)
+{
+ char *tmp = NULL;
+ int sts;
+
+ if ((tmp = malloc(tag_len + 1)) == NULL)
+ return -oserror();
+ strncpy(tmp, tag, tag_len);
+ tmp[tag_len] = '\0';
+ sts = pmtraceend(tmp);
+ free(tmp);
+ return sts;
+}
+
+int
+pmtraceabort_(const char *tag, int tag_len)
+{
+ char *tmp = NULL;
+ int sts;
+
+ if ((tmp = malloc(tag_len + 1)) == NULL)
+ return -oserror();
+ strncpy(tmp, tag, tag_len);
+ tmp[tag_len] = '\0';
+ sts = pmtraceabort(tmp);
+ free(tmp);
+ return sts;
+}
+
+int
+pmtracepoint_(const char *tag, int tag_len)
+{
+ char *tmp = NULL;
+ int sts;
+
+ if ((tmp = malloc(tag_len + 1)) == NULL)
+ return -oserror();
+ strncpy(tmp, tag, tag_len);
+ tmp[tag_len] = '\0';
+ sts = pmtracepoint(tmp);
+ free(tmp);
+ return sts;
+}
+
+int
+pmtracecounter_(const char *tag, double *value, int tag_len)
+{
+ char *tmp = NULL;
+ int sts;
+
+ if ((tmp = malloc(tag_len + 1)) == NULL)
+ return -oserror();
+ strncpy(tmp, tag, tag_len);
+ tmp[tag_len] = '\0';
+ sts = pmtracecounter(tmp, *value);
+ free(tmp);
+ return sts;
+}
+
+int
+#ifdef __GNUC__
+pmtraceobs_(const char *tag, int tag_len, double *value)
+#else
+pmtraceobs_(const char *tag, double *value, int tag_len)
+#endif
+{
+ char *tmp = NULL;
+ int sts;
+
+ if ((tmp = malloc(tag_len + 1)) == NULL)
+ return -oserror();
+ strncpy(tmp, tag, tag_len);
+ tmp[tag_len] = '\0';
+ sts = pmtraceobs(tmp, *value);
+ free(tmp);
+ return sts;
+}
+
+void
+pmtraceerrstr_(int *code, char *msg, int msg_len)
+{
+ char *tmp;
+ int len;
+
+ tmp = pmtraceerrstr(*code);
+ len = (int)strlen(tmp);
+ len = (len < msg_len ? len : msg_len);
+
+ strncpy(msg, tmp, len);
+ for (; len < msg_len; len++) /* blank fill */
+ msg[len-1] = ' ';
+}
+
+int
+pmtracestate_(int *code)
+{
+ return pmtracestate(*code);
+}
diff --git a/src/libpcp_trace/src/hash.c b/src/libpcp_trace/src/hash.c
new file mode 100644
index 0000000..d323285
--- /dev/null
+++ b/src/libpcp_trace/src/hash.c
@@ -0,0 +1,181 @@
+/*
+ * hash.c - hash table used by trace pmda and libpcp_trace
+ *
+ * Copyright (c) 1997-2000 Silicon Graphics, Inc. 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.
+ */
+
+#include "pmapi.h"
+#include "hash.h"
+
+
+static unsigned int
+hashindex(const char *key, size_t tablesize)
+{
+ unsigned int hash, i;
+ char *p = (char *)key;
+
+ /* use first few chars for hash table distribution */
+ for (i=0, hash=0; i < 5 && *p; i++)
+ hash = (hash << PM_HASH_SHFT) - hash + *p++;
+
+#ifdef DESPERATE
+ fprintf(stderr, "Generated hash number: %d (%s)\n", hash % tablesize, key);
+#endif
+ return hash % (unsigned int)tablesize;
+}
+
+
+int
+__pmhashinit(__pmHashTable *t, size_t tsize, size_t esize,
+ __pmHashCmpFunc cmp, __pmHashDelFunc del)
+{
+ size_t blocksize;
+
+ t->tsize = tsize;
+ t->esize = esize;
+ t->entries = 0;
+ t->cmp = cmp;
+ t->del = del;
+ if (t->tsize <= 0) /* use default */
+ t->tsize = PM_HASH_SIZE;
+ blocksize = sizeof(__pmHashEnt *) * t->tsize;
+ if ((t->rows = (__pmHashEnt **)malloc(blocksize)) == NULL)
+ return -oserror();
+ memset((void *)t->rows, 0, blocksize);
+ return 0;
+}
+
+
+static int
+hashalloc(__pmHashTable *t, __pmHashEnt **entry)
+{
+ int e;
+
+ if ((*entry = (__pmHashEnt *)malloc(sizeof(__pmHashEnt))) == NULL)
+ return -oserror();
+ if (((*entry)->ent = malloc(t->esize)) == NULL) {
+ e = -oserror();
+ free(*entry);
+ *entry = NULL;
+ return e;
+ }
+ (*entry)->next = NULL;
+ return 0;
+}
+
+
+void
+__pmhashtrunc(__pmHashTable *t)
+{
+ if (t == NULL || t->rows == NULL || t->entries <= 0)
+ return;
+ else {
+ __pmHashEnt *tmp, *e;
+ int i;
+
+ for (i = 0; i < PM_HASH_SIZE; i++) {
+ e = t->rows[i];
+ while (e != NULL) {
+ tmp = e;
+ e = e->next;
+ if (tmp->ent != NULL) {
+ t->del(tmp->ent);
+ tmp->ent = NULL;
+ }
+ if (tmp) {
+ free(tmp);
+ tmp = NULL;
+ }
+ }
+ t->rows[i] = NULL;
+ }
+ memset((void *)t->rows, 0, sizeof(__pmHashEnt *)*t->tsize);
+ t->entries = 0;
+ }
+}
+
+
+void *
+__pmhashlookup(__pmHashTable *t, const char *key, void *result)
+{
+ __pmHashEnt *e;
+ int index;
+
+ if (t->entries == 0)
+ return NULL;
+
+ index = hashindex(key, t->tsize);
+ e = t->rows[index];
+ while (e != NULL) {
+ if (t->cmp(e->ent, result))
+ break;
+ e = e->next;
+ }
+ if (e == NULL)
+ return NULL;
+ return e->ent;
+}
+
+
+int
+__pmhashinsert(__pmHashTable *t, const char *key, void *entry)
+{
+ __pmHashEnt *hash = NULL;
+ int index, sts;
+
+ if ((sts = hashalloc(t, &hash)) < 0)
+ return sts;
+
+ index = hashindex(key, t->tsize);
+
+ /* stick at head of list (locality of reference) */
+ memcpy(hash->ent, entry, t->esize);
+ if (t->rows[index]) {
+ hash->next = t->rows[index]->next;
+ t->rows[index]->next = hash;
+ }
+ else {
+ t->rows[index] = hash;
+ t->rows[index]->next = NULL;
+ }
+ t->entries++;
+#ifdef DESPERATE
+ fprintf(stderr, "Insert: %s at 0x%x (key=%d entry=%d)\n",
+ key, &t->rows[index], index, t->entries);
+#endif
+ return 0;
+}
+
+
+void
+__pmhashtraverse(__pmHashTable *t, __pmHashIterFunc func)
+{
+ __pmHashEnt *e = NULL;
+ unsigned int i, hits;
+
+ if (t == NULL || func == NULL)
+ return;
+
+ for (i = 0, hits = 0; i < PM_HASH_SIZE && hits < t->entries; i++) {
+ e = t->rows[i];
+ while (e != NULL && hits < t->entries) {
+ hits++;
+ if (e->ent != NULL)
+ func(t, e->ent);
+ e = e->next;
+ }
+ }
+#ifdef DESPERATE
+ fprintf(stderr, "Traverse: looped %d times\n", i);
+#endif
+}
diff --git a/src/libpcp_trace/src/hash.h b/src/libpcp_trace/src/hash.h
new file mode 100644
index 0000000..dfe9be0
--- /dev/null
+++ b/src/libpcp_trace/src/hash.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 1997 Silicon Graphics, Inc. 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.
+ */
+
+#ifndef _TRACE_HASH_H
+#define _TRACE_HASH_H
+
+typedef int (*__pmHashCmpFunc)(void *, void *);
+typedef int (*__pmHashKeyCmpFunc)(void *, const char *);
+typedef void (*__pmHashDelFunc)(void *);
+
+struct __pmHashEl {
+ void *ent;
+ struct __pmHashEl *next;
+};
+typedef struct __pmHashEl __pmHashEnt;
+
+struct __pmHashTab {
+ size_t tsize;
+ size_t esize;
+ unsigned int entries;
+ __pmHashCmpFunc cmp;
+ __pmHashDelFunc del;
+ __pmHashEnt **rows;
+};
+typedef struct __pmHashTab __pmHashTable;
+
+extern int __pmhashinit(__pmHashTable *, size_t, size_t, __pmHashCmpFunc, __pmHashDelFunc);
+
+extern void __pmhashtrunc(__pmHashTable *);
+
+extern int __pmhashinsert(__pmHashTable *, const char *, void *);
+
+extern void *__pmhashlookup(__pmHashTable *, const char *, void *);
+
+typedef void (*__pmHashIterFunc)(__pmHashTable *, void *);
+extern void __pmhashtraverse(__pmHashTable *, __pmHashIterFunc);
+
+#ifndef PM_HASH_TUNE
+#define PM_HASH_SHFT 5
+#define PM_HASH_SIZE 31
+#endif
+
+#endif /* _TRACE_HASH_H */
diff --git a/src/libpcp_trace/src/p_ack.c b/src/libpcp_trace/src/p_ack.c
new file mode 100644
index 0000000..9bf67f9
--- /dev/null
+++ b/src/libpcp_trace/src/p_ack.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1997-2000 Silicon Graphics, Inc. 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.
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "trace.h"
+#include "trace_dev.h"
+
+
+/*
+ * PDU for general receive acknowledgement (TRACE_PDU_ACK)
+ */
+typedef struct {
+ __pmTracePDUHdr hdr;
+ __int32_t data; /* ack for specific PDU type / error code */
+} ack_t;
+
+int
+__pmtracesendack(int fd, __int32_t data)
+{
+ ack_t *pp;
+
+ if (__pmstate & PMTRACE_STATE_NOAGENT) {
+ fprintf(stderr, "__pmtracesendack: sending acka (skipped)\n");
+ return 0;
+ }
+
+ if ((pp = (ack_t *)__pmtracefindPDUbuf(sizeof(ack_t))) == NULL)
+ return -oserror();
+ pp->hdr.len = sizeof(ack_t);
+ pp->hdr.type = TRACE_PDU_ACK;
+ pp->data = htonl(data);
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_PDU)
+ fprintf(stderr, "__pmtracesendack(data=%d)\n",
+ (int)pp->data);
+#endif
+ return __pmtracexmitPDU(fd, (__pmTracePDU *)pp);
+}
+
+int
+__pmtracedecodeack(__pmTracePDU *pdubuf, __int32_t *data)
+{
+ ack_t *pp;
+ char *pduend;
+
+ pp = (ack_t *)pdubuf;
+ pduend = (char *)pdubuf + pp->hdr.len;
+
+ if (pduend - (char*)pp != sizeof(ack_t))
+ return PMTRACE_ERR_IPC;
+
+ *data = ntohl(pp->data);
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_PDU)
+ fprintf(stderr, "__pmtracedecodeack -> data=%d\n",
+ (int)*data);
+#endif
+ return 0;
+}
diff --git a/src/libpcp_trace/src/p_data.c b/src/libpcp_trace/src/p_data.c
new file mode 100644
index 0000000..7780a48
--- /dev/null
+++ b/src/libpcp_trace/src/p_data.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 1997-2002 Silicon Graphics, Inc. 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.
+ *
+ * Assumptions:
+ * 1. "double" is 64-bits
+ * 2. "double" is IEEE format and needs endian conversion (like a
+ * 64-bit integer, but no other format conversion
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "trace.h"
+#include "trace_dev.h"
+
+/*
+ * PDU for all trace data updates (TRACE_PDU_DATA)
+ *
+ * note "taglen" includes the null-byte terminator
+ */
+typedef struct {
+ __pmTracePDUHdr hdr;
+ struct {
+#ifdef HAVE_BITFIELDS_LTOR
+ unsigned int version : 8;
+ unsigned int taglen : 8;
+ unsigned int tagtype : 8;
+ unsigned int protocol : 1;
+ unsigned int pad : 7;
+#else
+ unsigned int pad : 7;
+ unsigned int protocol : 1;
+ unsigned int tagtype : 8;
+ unsigned int taglen : 8;
+ unsigned int version : 8;
+#endif
+ } bits;
+ /*
+ * avoid struct padding traps!
+ *
+ * what really follows is a double (data) and then the tag
+ */
+} tracedata_t;
+
+#ifdef HAVE_NETWORK_BYTEORDER
+#define trace_htonll(a) do { } while (0) /* noop */
+#define trace_ntohll(a) do { } while (0) /* noop */
+#else
+static void
+trace_htonll(char *p)
+{
+ char c;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ c = p[i];
+ p[i] = p[7-i];
+ p[7-i] = c;
+ }
+}
+#define trace_ntohll(v) trace_htonll(v)
+#endif
+
+int
+__pmtracesenddata(int fd, char *tag, int taglen, int tagtype, double data)
+{
+ tracedata_t *pp = NULL;
+ size_t need = 0;
+ char *cp;
+ int *ip;
+
+ if (taglen <= 0)
+ return PMTRACE_ERR_IPC;
+ else if (__pmstate & PMTRACE_STATE_NOAGENT) {
+ fprintf(stderr, "__pmtracesenddata: sending data (skipped)\n");
+ return 0;
+ }
+
+ /*
+ * pad to the next __pmTracePDU boundary
+ */
+ need = sizeof(tracedata_t) + sizeof(double) + sizeof(__pmTracePDU)*((taglen - 1 + sizeof(__pmTracePDU))/sizeof(__pmTracePDU));
+
+ if ((pp = (tracedata_t *)__pmtracefindPDUbuf((int)need)) == NULL)
+ return -oserror();
+ pp->hdr.len = (int)need;
+ pp->hdr.type = TRACE_PDU_DATA;
+ pp->bits.taglen = taglen;
+ pp->bits.tagtype = tagtype;
+ pp->bits.version = TRACE_PDU_VERSION;
+ if (__pmtraceprotocol(TRACE_PROTOCOL_QUERY) == TRACE_PROTOCOL_SYNC)
+ pp->bits.protocol = 1;
+ else
+ pp->bits.protocol = 0;
+ pp->bits.pad = 0;
+ ip = (int *)&pp->bits;
+ *ip = htonl(*ip);
+
+ cp = (char *)pp;
+ cp += sizeof(tracedata_t);
+ memcpy((void *)cp, (void *)&data, sizeof(double));
+ trace_htonll(cp); /* send in network byte order */
+ cp += sizeof(double);
+ strcpy(cp, tag);
+#ifdef PCP_DEBUG
+ if ((taglen % sizeof(__pmTracePDU)) != 0) {
+ /* for Purify */
+ int pad;
+ char *padp = cp + taglen;
+ for (pad = sizeof(__pmTracePDU) - 1; pad >= (taglen % sizeof(__pmTracePDU)); pad--)
+ *padp++ = '~'; /* buffer end */
+ }
+#endif
+
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_PDU)
+ fprintf(stderr, "__pmtracesenddata(tag=\"%s\", data=%f)\n", tag, data);
+#endif
+
+ return __pmtracexmitPDU(fd, (__pmTracePDU *)pp);
+}
+
+int
+__pmtracedecodedata(__pmTracePDU *pdubuf, char **tag, int *taglenp,
+ int *tagtype, int *protocol, double *data)
+{
+ tracedata_t *pp;
+ int *ip;
+ char *cp;
+ char *pduend;
+ int taglen;
+
+ if (pdubuf == NULL)
+ return PMTRACE_ERR_IPC;
+
+ pp = (tracedata_t *)pdubuf;
+ pduend = (char *)pdubuf + pp->hdr.len;
+
+ if (pduend - (char*)pp < sizeof(tracedata_t) + sizeof(double))
+ return PMTRACE_ERR_IPC;
+
+ ip = (int *)&pp->bits;
+ *ip = ntohl(*ip);
+ taglen = pp->bits.taglen;
+ if (pp->bits.version != TRACE_PDU_VERSION)
+ return PMTRACE_ERR_VERSION;
+ if (taglen <= 0 || taglen >= CHAR_MAX - 1 || taglen > pp->hdr.len)
+ return PMTRACE_ERR_IPC;
+ if (pduend - (char *)pp < sizeof(tracedata_t) + sizeof(double) + taglen)
+ return PMTRACE_ERR_IPC;
+ *taglenp = taglen;
+ *tagtype = pp->bits.tagtype;
+ *protocol = pp->bits.protocol;
+
+ cp = (char *)pp;
+ cp += sizeof(tracedata_t);
+ memcpy((void *)data, (void *)cp, sizeof(double));
+ trace_ntohll((char *)data); /* receive in network byte order */
+ cp += sizeof(double);
+ if ((*tag = (char *)malloc(taglen+1)) == NULL)
+ return -oserror();
+ strncpy(*tag, cp, taglen);
+ (*tag)[taglen] = '\0';
+
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_PDU)
+ fprintf(stderr, "__pmtracedecodedata -> tag=\"%s\" data=%f\n", *tag, *data);
+#endif
+ return 0;
+}
diff --git a/src/libpcp_trace/src/pdu.c b/src/libpcp_trace/src/pdu.c
new file mode 100644
index 0000000..ad3c039
--- /dev/null
+++ b/src/libpcp_trace/src/pdu.c
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 1997-2000,2003 Silicon Graphics, Inc. All Rights Reserved.
+ * Copyright (c) 2012 Red Hat. 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.
+ */
+
+#include <signal.h>
+#include "pmapi.h"
+#include "impl.h"
+#include "trace.h"
+#include "trace_dev.h"
+
+typedef struct {
+ __pmTracePDU *pdubuf;
+ int len;
+} more_ctl;
+static more_ctl *more;
+static int maxfd = -1;
+
+static char *
+pdutypestr(int type)
+{
+ if (type == TRACE_PDU_ACK) return "ACK";
+ else if (type == TRACE_PDU_DATA) return "DATA";
+ else {
+ static char buf[20];
+ snprintf(buf, sizeof(buf), "TYPE-%d?", type);
+ return buf;
+ }
+}
+
+static void
+moreinput(int fd, __pmTracePDU *pdubuf, int len)
+{
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_PDU) {
+ __pmTracePDUHdr *php = (__pmTracePDUHdr *)pdubuf;
+ __pmTracePDU *p;
+ int j, jend;
+ char *q;
+
+ jend = (php->len+(int)sizeof(__pmTracePDU)-1)/(int)sizeof(__pmTracePDU);
+ fprintf(stderr, "moreinput: fd=%d pdubuf=0x%p len=%d\n",
+ fd, pdubuf, len);
+ fprintf(stderr, "Piggy-back PDU: %s addr=0x%p len=%d from=%d",
+ pdutypestr(php->type), php, php->len, php->from);
+ fprintf(stderr, "%03d: ", 0);
+ p = (__pmTracePDU *)php;
+
+ /* for Purify ... */
+ q = (char *)p + php->len;
+ while (q < (char *)p + jend*sizeof(__pmTracePDU))
+ *q++ = '~'; /* buffer end */
+
+ for (j = 0; j < jend; j++) {
+ if ((j % 8) == 0)
+ fprintf(stderr, "\n%03d: ", j);
+ fprintf(stderr, "%8x ", p[j]);
+ }
+ putc('\n', stderr);
+ }
+#endif
+ if (fd > maxfd) {
+ int next = maxfd + 1;
+ if ((more = (more_ctl *)realloc(more, (fd+1)*sizeof(more[0]))) == NULL)
+{
+ fprintf(stderr, "realloc failed (%d bytes): %s\n",
+ (fd+1)*(int)sizeof(more[0]), osstrerror());
+ return;
+ }
+ maxfd = fd;
+ while (next <= maxfd) {
+ more[next].pdubuf = NULL;
+ next++;
+ }
+ }
+
+ __pmtracepinPDUbuf(pdubuf);
+ more[fd].pdubuf = pdubuf;
+ more[fd].len = len;
+}
+
+int
+__pmtracemoreinput(int fd)
+{
+ if (fd < 0 || fd > maxfd)
+ return 0;
+
+ return more[fd].pdubuf == NULL ? 0 : 1;
+}
+
+void
+__pmtracenomoreinput(int fd)
+{
+ if (fd < 0 || fd > maxfd)
+ return;
+
+ if (more[fd].pdubuf != NULL) {
+ __pmtraceunpinPDUbuf(more[fd].pdubuf);
+ more[fd].pdubuf = NULL;
+ }
+}
+
+static int
+pduread(int fd, char *buf, int len, int mode, int timeout)
+{
+ /*
+ * handle short reads that may split a PDU ...
+ */
+ int status = 0;
+ int have = 0;
+ __pmFdSet onefd;
+ static int done_default = 0;
+ static struct timeval def_wait = { 10, 0 };
+
+ if (timeout == TRACE_TIMEOUT_DEFAULT) {
+ if (!done_default) {
+ double def_timeout;
+ char *timeout_str;
+ char *end_ptr;
+
+ if ((timeout_str = getenv(TRACE_ENV_REQTIMEOUT)) != NULL) {
+ def_timeout = strtod(timeout_str, &end_ptr);
+ if (*end_ptr != '\0' || def_timeout < 0.0) {
+ status = PMTRACE_ERR_ENVFORMAT;
+ return status;
+ }
+ else {
+ def_wait.tv_sec = (int)def_timeout; /* truncate -> secs */
+ if (def_timeout > (double)def_wait.tv_sec)
+ def_wait.tv_usec = (long)((def_timeout -
+ (double)def_wait.tv_sec) * 1000000);
+ else
+ def_wait.tv_usec = 0;
+ }
+ }
+ done_default = 1;
+ }
+ }
+
+ while (len) {
+ struct timeval wait;
+ /*
+ * either never timeout (i.e. block forever), or timeout
+ */
+ if (timeout != TRACE_TIMEOUT_NEVER) {
+ if (timeout > 0) {
+ wait.tv_sec = timeout;
+ wait.tv_usec = 0;
+ }
+ else
+ wait = def_wait;
+ __pmFD_ZERO(&onefd);
+ __pmFD_SET(fd, &onefd);
+ status = __pmSelectRead(fd+1, &onefd, &wait);
+ if (status == 0)
+ return PMTRACE_ERR_TIMEOUT;
+ else if (status < 0) {
+ setoserror(neterror());
+ return status;
+ }
+ }
+ status = (int)__pmRead(fd, buf, len);
+ if (status <= 0) { /* EOF or error */
+ setoserror(neterror());
+ return status;
+ }
+ if (mode == -1)
+ /* special case, see __pmtracegetPDU */
+ return status;
+ have += status;
+ buf += status;
+ len -= status;
+ }
+
+ return have;
+}
+
+
+/*
+ * Because the default handler for SIGPIPE is to exit, we always want a handler
+ * installed to override that so that the write() just returns an error. The
+ * problem is that the user might have installed one prior to the first write()
+ * or may install one at some later stage. This doesn't matter. As long as a
+ * handler other than SIG_DFL is there, all will be well. The first time that
+ * __pmtracexmitPDU is called, install SIG_IGN as the handler for SIGPIPE.
+ * If the user had already changed the handler from SIG_DFL, put back what was
+ * there before.
+ */
+
+int
+__pmtracexmitPDU(int fd, __pmTracePDU *pdubuf)
+{
+ int n, len;
+ __pmTracePDUHdr *php = (__pmTracePDUHdr *)pdubuf;
+
+#if defined(HAVE_SIGPIPE)
+ SIG_PF user_onpipe;
+ user_onpipe = signal(SIGPIPE, SIG_IGN);
+ if (user_onpipe != SIG_DFL) /* put user handler back */
+ signal(SIGPIPE, user_onpipe);
+#endif
+
+ php->from = (__int32_t)getpid();
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_PDU) {
+ int j;
+ int jend = (php->len+(int)sizeof(__pmTracePDU)-1)/(int)sizeof(__pmTracePDU);
+ char *p;
+
+ /* for Purify ... */
+ p = (char *)pdubuf + php->len;
+ while (p < (char *)pdubuf + jend*sizeof(__pmTracePDU))
+ *p++ = '~'; /* buffer end */
+
+ fprintf(stderr, "[%d]__pmtracexmitPDU: %s fd=%d len=%d",
+ php->from, pdutypestr(php->type), fd, php->len);
+ for (j = 0; j < jend; j++) {
+ if ((j % 8) == 0)
+ fprintf(stderr, "\n%03d: ", j);
+ fprintf(stderr, "%8x ", pdubuf[j]);
+ }
+ putc('\n', stderr);
+ }
+#endif
+ len = php->len;
+
+ php->len = htonl(php->len);
+ php->from = htonl(php->from);
+ php->type = htonl(php->type);
+ n = (int)__pmWrite(fd, pdubuf, len);
+ php->len = ntohl(php->len);
+ php->from = ntohl(php->from);
+ php->type = ntohl(php->type);
+
+ if (n != len)
+ return -oserror();
+
+ return n;
+}
+
+
+int
+__pmtracegetPDU(int fd, int timeout, __pmTracePDU **result)
+{
+ int need, len;
+ char *handle;
+ static int maxsize = TRACE_PDU_CHUNK;
+ __pmTracePDU *pdubuf;
+ __pmTracePDU *pdubuf_prev;
+ __pmTracePDUHdr *php;
+
+ /*
+ * This stuff is a little tricky. What we try to do is read()
+ * an amount of data equal to the largest PDU we have (or are
+ * likely to have) seen thus far. In the majority of cases
+ * this returns exactly one PDU's worth, i.e. read() returns
+ * a length equal to php->len.
+ *
+ * For this to work, we have a special "mode" of -1
+ * to pduread() which means read, but return after the
+ * first read(), rather than trying to read up to the request
+ * length with multiple read()s, which would of course "hang"
+ * after the first PDU arrived.
+ *
+ * We need to handle the following tricky cases:
+ * 1. We get _more_ than we need for a single PDU -- happens
+ * when PDU's arrive together. This requires "moreinput"
+ * to handle leftovers here (it gets even uglier if we
+ * have part, but not all of the second PDU).
+ * 2. We get _less_ than we need for a single PDU -- this
+ * requires at least another read(), and possibly acquiring
+ * another pdubuf and doing a memcpy() for the partial PDU
+ * from the earlier call.
+ */
+ if (__pmtracemoreinput(fd)) {
+ /* some leftover from last time ... handle -> start of PDU */
+ pdubuf = more[fd].pdubuf;
+ len = more[fd].len;
+ __pmtracenomoreinput(fd);
+ }
+ else {
+ if ((pdubuf = __pmtracefindPDUbuf(maxsize)) == NULL)
+ return -oserror();
+ len = pduread(fd, (void *)pdubuf, maxsize, -1, timeout);
+ }
+ php = (__pmTracePDUHdr *)pdubuf;
+
+ if (len < (int)sizeof(__pmTracePDUHdr)) {
+ if (len == -1) {
+ if (oserror() == ECONNRESET||
+ oserror() == ETIMEDOUT || oserror() == ENETDOWN ||
+ oserror() == ENETUNREACH || oserror() == EHOSTDOWN ||
+ oserror() == EHOSTUNREACH || oserror() == ECONNREFUSED)
+ /*
+ * failed as a result of pmdatrace exiting and the
+ * connection being reset, or as a result of the kernel
+ * ripping down the connection (most likely because the
+ * host at the other end just took a dive)
+ *
+ * treat this like end of file on input
+ *
+ * from irix/kern/fs/nfs/bds.c seems like all of the
+ * following are peers here:
+ * ECONNRESET (pmdatrace terminated?)
+ * ETIMEDOUT ENETDOWN ENETUNREACH EHOSTDOWN EHOSTUNREACH
+ * ECONNREFUSED
+ * peers for bds but not here:
+ * ENETRESET ENONET ESHUTDOWN (cache_fs only?)
+ * ECONNABORTED (accept, user req only?)
+ * ENOTCONN (udp?)
+ * EPIPE EAGAIN (nfs, bds & ..., but not ip or tcp?)
+ */
+ len = 0;
+ else
+ fprintf(stderr, "__pmtracegetPDU: fd=%d hdr: %s",
+ fd, osstrerror());
+ }
+ else if (len > 0)
+ fprintf(stderr, "__pmtracegetPDU: fd=%d hdr: len=%d, not %d?",
+ fd, len, (int)sizeof(__pmTracePDUHdr));
+ else if (len == PMTRACE_ERR_TIMEOUT)
+ return PMTRACE_ERR_TIMEOUT;
+ else if (len < 0)
+ fprintf(stderr, "__pmtracegetPDU: fd=%d hdr: %s", fd, pmtraceerrstr(len));
+ return len ? PMTRACE_ERR_IPC : 0;
+ }
+
+ php->len = ntohl(php->len);
+ if (php->len < 0) {
+ fprintf(stderr, "__pmtracegetPDU: fd=%d illegal len=%d in hdr\n", fd, php->len);
+ return PMTRACE_ERR_IPC;
+ }
+
+ if (len == php->len)
+ /* return below */
+ ;
+ else if (len > php->len) {
+ /*
+ * read more than we need for this one, save it up for next time
+ */
+ handle = (char *)pdubuf;
+ moreinput(fd, (__pmTracePDU *)&handle[php->len], len - php->len);
+ }
+ else {
+ int tmpsize;
+
+ /*
+ * need to read more ...
+ */
+ __pmtracepinPDUbuf(pdubuf);
+ pdubuf_prev = pdubuf;
+ if (php->len > maxsize)
+ tmpsize = TRACE_PDU_CHUNK * ( 1 + php->len / TRACE_PDU_CHUNK);
+ else
+ tmpsize = maxsize;
+ if ((pdubuf = __pmtracefindPDUbuf(tmpsize)) == NULL) {
+ __pmtraceunpinPDUbuf(pdubuf_prev);
+ return -oserror();
+ }
+ if (php->len > maxsize)
+ maxsize = tmpsize;
+ memmove((void *)pdubuf, (void *)php, len);
+ __pmtraceunpinPDUbuf(pdubuf_prev);
+ php = (__pmTracePDUHdr *)pdubuf;
+ need = php->len - len;
+ handle = (char *)pdubuf;
+ /* block until all of the PDU is received this time */
+ len = pduread(fd, (void *)&handle[len], need, 0, timeout);
+ if (len != need) {
+ if (len == PMTRACE_ERR_TIMEOUT)
+ return PMTRACE_ERR_TIMEOUT;
+ if (len < 0)
+ fprintf(stderr, "__pmtracegetPDU: error (%d) fd=%d: %s\n", (int)oserror(), fd, osstrerror());
+ else
+ fprintf(stderr, "__pmtracegetPDU: len=%d, not %d? (fd=%d)\n", len, need, fd);
+ fprintf(stderr, "hdr: len=0x%08x type=0x%08x from=0x%08x\n",
+ php->len, (int)ntohl(php->type), (int)ntohl(php->from));
+ return PMTRACE_ERR_IPC;
+ }
+ }
+
+ *result = (__pmTracePDU *)php;
+ php->type = ntohl(php->type);
+ php->from = ntohl(php->from);
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_PDU) {
+ int j;
+ int jend = (int)(php->len+(int)sizeof(__pmTracePDU)-1)/(int)sizeof(__pmTracePDU);
+ char *p;
+
+ /* for Purify ... */
+ p = (char *)*result + php->len;
+ while (p < (char *)*result + jend*sizeof(__pmTracePDU))
+ *p++ = '~'; /* buffer end */
+
+ fprintf(stderr, "[%" FMT_PID "]__pmtracegetPDU: %s fd=%d len=%d from=%d",
+ getpid(), pdutypestr(php->type), fd, php->len, php->from);
+
+ for (j = 0; j < jend; j++) {
+ if ((j % 8) == 0)
+ fprintf(stderr, "\n%03d: ", j);
+ fprintf(stderr, "%8x ", (*result)[j]);
+ }
+ putc('\n', stderr);
+ }
+#endif
+
+ return php->type;
+}
diff --git a/src/libpcp_trace/src/pdubuf.c b/src/libpcp_trace/src/pdubuf.c
new file mode 100644
index 0000000..0440275
--- /dev/null
+++ b/src/libpcp_trace/src/pdubuf.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 1995 Silicon Graphics, Inc. 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "trace.h"
+#include "trace_dev.h"
+
+
+typedef struct bufctl {
+ struct bufctl *bc_next;
+ int bc_size;
+ int bc_pincnt;
+ char *bc_buf;
+ char *bc_bufend;
+} bufctl_t;
+
+static bufctl_t *buf_free;
+static bufctl_t *buf_pin;
+static bufctl_t *buf_pin_tail;
+
+#ifdef PMTRACE_DEBUG
+static void
+pdubufdump(void)
+{
+ bufctl_t *pcp;
+
+ if (buf_free != NULL) {
+ fprintf(stderr, " free pdubuf[size]:");
+ for (pcp = buf_free; pcp != NULL; pcp = pcp->bc_next)
+ fprintf(stderr, " 0x%p[%d]", pcp->bc_buf, pcp->bc_size);
+ fputc('\n', stderr);
+ }
+
+ if (buf_pin != NULL) {
+ fprintf(stderr, " pinned pdubuf[pincnt]:");
+ for (pcp = buf_pin; pcp != NULL; pcp = pcp->bc_next)
+ fprintf(stderr, " 0x%p[%d]", pcp->bc_buf, pcp->bc_pincnt);
+ fputc('\n', stderr);
+ }
+}
+#endif
+
+__pmTracePDU *
+__pmtracefindPDUbuf(int need)
+{
+ bufctl_t *pcp;
+
+ for (pcp = buf_free; pcp != NULL; pcp = pcp->bc_next) {
+ if (pcp->bc_size >= need)
+ break;
+ }
+ if (pcp == NULL) {
+ if ((pcp = (bufctl_t *)malloc(sizeof(*pcp))) == NULL)
+ return NULL;
+ pcp->bc_pincnt = 0;
+ pcp->bc_size = TRACE_PDU_CHUNK * (1 + need/TRACE_PDU_CHUNK);
+ if ((pcp->bc_buf = (char *)valloc(pcp->bc_size)) == NULL) {
+ free(pcp);
+ return NULL;
+ }
+ pcp->bc_next = buf_free;
+ pcp->bc_bufend = &pcp->bc_buf[pcp->bc_size];
+ buf_free = pcp;
+ }
+
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_PDUBUF) {
+ fprintf(stderr, "__pmtracefindPDUbuf(%d) -> 0x%p\n", need, pcp->bc_buf);
+ pdubufdump();
+ }
+#endif
+
+ return (__pmTracePDU *)pcp->bc_buf;
+}
+
+void
+__pmtracepinPDUbuf(void *handle)
+{
+ bufctl_t *pcp;
+ bufctl_t *prior = NULL;
+
+ for (pcp = buf_free; pcp != NULL; pcp = pcp->bc_next) {
+ if (pcp->bc_buf <= (char *)handle && (char *)handle < pcp->bc_bufend)
+ break;
+ prior = pcp;
+ }
+
+ if (pcp != NULL) {
+ /* first pin for this buffer, move between lists */
+ if (prior == NULL)
+ buf_free = pcp->bc_next;
+ else
+ prior->bc_next = pcp->bc_next;
+ pcp->bc_next = NULL;
+ if (buf_pin_tail != NULL)
+ buf_pin_tail->bc_next = pcp;
+ buf_pin_tail = pcp;
+ if (buf_pin == NULL)
+ buf_pin = pcp;
+ pcp->bc_pincnt = 1;
+ }
+ else {
+ for (pcp = buf_pin; pcp != NULL; pcp = pcp->bc_next) {
+ if (pcp->bc_buf <= (char *)handle && (char *)handle < pcp->bc_bufend)
+ break;
+ }
+ if (pcp != NULL)
+ pcp->bc_pincnt++;
+ else {
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_PDUBUF) {
+ fprintf(stderr, "__pmtracepinPDUbuf: 0x%p not in pool!", handle);
+ pdubufdump();
+ }
+#endif
+ return;
+ }
+ }
+
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_PDUBUF)
+ fprintf(stderr, "__pmtracepinPDUbuf(0x%p) -> pdubuf=0x%p, cnt=%d\n",
+ handle, pcp->bc_buf, pcp->bc_pincnt);
+#endif
+ return;
+}
+
+int
+__pmtraceunpinPDUbuf(void *handle)
+{
+ bufctl_t *pcp;
+ bufctl_t *prior = NULL;
+
+ for (pcp = buf_pin; pcp != NULL; pcp = pcp->bc_next) {
+ if (pcp->bc_buf <= (char *)handle && (char *)handle < &pcp->bc_buf[pcp->bc_size])
+ break;
+ prior = pcp;
+ }
+ if (pcp == NULL) {
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_PDUBUF) {
+ fprintf(stderr, "__pmtraceunpinPDUbuf(0x%p) -> fails\n", handle);
+ pdubufdump();
+ }
+#endif
+ return 0;
+ }
+
+ if (--pcp->bc_pincnt == 0) {
+ if (prior == NULL)
+ buf_pin = pcp->bc_next;
+ else
+ prior->bc_next = pcp->bc_next;
+ if (buf_pin_tail == pcp)
+ buf_pin_tail = prior;
+
+ pcp->bc_next = buf_free;
+ buf_free = pcp;
+ }
+
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_PDUBUF)
+ fprintf(stderr, "__pmtraceunpinPDUbuf(0x%p) -> pdubuf=0x%p, pincnt=%d\n",
+ handle, pcp->bc_buf, pcp->bc_pincnt);
+#endif
+
+ return 1;
+}
diff --git a/src/libpcp_trace/src/trace.c b/src/libpcp_trace/src/trace.c
new file mode 100644
index 0000000..34a3135
--- /dev/null
+++ b/src/libpcp_trace/src/trace.c
@@ -0,0 +1,950 @@
+/*
+ * trace.c - client-side interface for trace PMDA
+ *
+ * Copyright (c) 2014 Red Hat.
+ * Copyright (c) 1997-2004 Silicon Graphics, Inc. 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.
+ */
+
+#include <inttypes.h>
+#include "pmapi.h"
+#include "impl.h"
+
+#if defined(HAVE_PTHREAD_H)
+#include <pthread.h>
+#elif defined(HAVE_ABI_MUTEX_H)
+#include <abi_mutex.h>
+#elif !defined(IS_MINGW)
+#error !bozo!
+#endif
+
+#include "hash.h"
+#include "trace.h"
+#include "trace_dev.h"
+
+static int _pmtimedout = 1;
+static time_t _pmttimeout = 0;
+
+static int _pmtraceconnect(int);
+static int _pmtracereconnect(void);
+static int _pmauxtraceconnect(void);
+static void _pmtraceupdatewait(void);
+static int _pmtracegetack(int, int);
+static int _pmtraceremaperr(int);
+static __uint64_t _pmtraceid(void);
+
+int __pmstate = PMTRACE_STATE_NONE;
+
+
+static double
+__pmtracetvsub(const struct timeval *a, const struct timeval *b)
+{
+ return (double)(a->tv_sec - b->tv_sec + (double)(a->tv_usec - b->tv_usec)/1000000.0);
+}
+
+/* transaction data unit */
+typedef struct {
+ __uint64_t id;
+ char *tag;
+ unsigned int tracetype : 7;
+ unsigned int inprogress : 1;
+ unsigned int taglength : 8;
+ unsigned int pad : 16;
+ struct timeval start; /* transaction started timestamp */
+ double data; /* time interval / -1 / user-defined */
+} _pmTraceLibdata;
+
+static int
+_pmlibcmp(void *a, void *b)
+{
+ _pmTraceLibdata *aa = (_pmTraceLibdata *)a;
+ _pmTraceLibdata *bb = (_pmTraceLibdata *)b;
+
+ if (!aa || !bb || (aa->id != bb->id))
+ return 0;
+ if (aa->tracetype != bb->tracetype)
+ return 0;
+ if (aa->id != bb->id)
+ return 0;
+ return !strcmp(aa->tag, bb->tag);
+}
+
+static void
+_pmlibdel(void *entry)
+{
+ _pmTraceLibdata *data = (_pmTraceLibdata *)entry;
+
+ if (data->tag)
+ free(data->tag);
+ if (data)
+ free(data);
+}
+
+static int __pmfd;
+static __pmHashTable _pmtable;
+
+#if defined(HAVE_PTHREAD_MUTEX_T)
+
+/* use portable pthreads for mutex */
+static pthread_mutex_t _pmtracelock;
+#define TRACE_LOCK_INIT pthread_mutex_init(&_pmtracelock, NULL)
+#define TRACE_LOCK pthread_mutex_lock(&_pmtracelock)
+#define TRACE_UNLOCK pthread_mutex_unlock(&_pmtracelock)
+
+#elif defined(HAVE_ABI_MUTEX_H)
+/* use an SGI spinlock for mutex */
+static abilock_t _pmtracelock;
+#define TRACE_LOCK_INIT init_lock(&_pmtracelock)
+#define TRACE_LOCK spin_lock(&_pmtracelock)
+#define TRACE_UNLOCK release_lock(&_pmtracelock)
+
+#elif defined(IS_MINGW)
+/* use native Win32 primitives */
+static HANDLE _pmtracelock;
+#define TRACE_LOCK_INIT (_pmtracelock = CreateMutex(NULL, FALSE, NULL), 0)
+#define TRACE_LOCK WaitForSingleObject(_pmtracelock, INFINITE)
+#define TRACE_UNLOCK ReleaseMutex(_pmtracelock)
+
+#else
+#error !bozo!
+#endif
+
+int
+pmtracebegin(const char *tag)
+{
+ static int first = 1;
+ _pmTraceLibdata *hptr;
+ _pmTraceLibdata hash;
+ int len, a_sts = 0, b_sts = 0, protocol;
+
+ if (tag == NULL || *tag == '\0')
+ return PMTRACE_ERR_TAGNAME;
+ if ((len = strlen(tag)+1) >= MAXTAGNAMELEN)
+ return PMTRACE_ERR_TAGLENGTH;
+
+ hash.tag = (char *)tag;
+ hash.taglength = len;
+ hash.id = _pmtraceid();
+ hash.tracetype = TRACE_TYPE_TRANSACT;
+
+ /*
+ * We need to do both the connect and hash table manipulation,
+ * otherwise the reconnect isn't reliable and the hash table
+ * (potentially) becomes completely wrong, and we reject some
+ * transact calls which actually were in a valid call sequence.
+ */
+
+ protocol = __pmtraceprotocol(TRACE_PROTOCOL_QUERY);
+
+ if (_pmtimedout && (a_sts = _pmtraceconnect(1)) < 0) {
+ if (first || protocol == TRACE_PROTOCOL_ASYNC)
+ return a_sts; /* exception to the rule */
+ a_sts = _pmtraceremaperr(a_sts);
+ if (a_sts == PMTRACE_ERR_IPC && protocol == TRACE_PROTOCOL_SYNC) {
+ _pmtimedout = 1; /* try reconnect */
+ a_sts = 0;
+ }
+ }
+ if (a_sts >= 0)
+ first = 0;
+
+ /* lock hash table for search and subsequent insert/update */
+ TRACE_LOCK;
+
+ if ((hptr = __pmhashlookup(&_pmtable, tag, &hash)) == NULL) {
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_API)
+ fprintf(stderr, "pmtracebegin: new transaction '%s' "
+ "(id=0x%" PRIx64 ")\n", tag, hash.id);
+#endif
+ hash.pad = 0;
+ if ((hash.tag = strdup(tag)) == NULL)
+ b_sts = -oserror();
+ __pmtimevalNow(&hash.start);
+ if (b_sts >= 0) {
+ hash.inprogress = 1;
+ b_sts = __pmhashinsert(&_pmtable, tag, &hash);
+ }
+ if (b_sts < 0 && hash.tag != NULL)
+ free(hash.tag);
+ }
+ else if (hptr->inprogress == 1)
+ b_sts = PMTRACE_ERR_INPROGRESS;
+ else if (hptr->tracetype != TRACE_TYPE_TRANSACT)
+ b_sts = PMTRACE_ERR_TAGTYPE;
+ else {
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_API)
+ fprintf(stderr, "pmtracebegin: updating transaction '%s' "
+ "(id=0x%" PRIx64 ")\n", tag, hash.id);
+#endif
+ __pmtimevalNow(&hptr->start);
+ hptr->inprogress = 1;
+ }
+
+ /* unlock hash table */
+ if (TRACE_UNLOCK != 0)
+ b_sts = -oserror();
+
+ if (a_sts < 0)
+ return a_sts;
+ return b_sts;
+}
+
+int
+pmtraceend(const char *tag)
+{
+ _pmTraceLibdata hash;
+ _pmTraceLibdata *hptr;
+ struct timeval now;
+ int len, protocol, sts = 0;
+
+ if (tag == NULL || *tag == '\0')
+ return PMTRACE_ERR_TAGNAME;
+ if ((len = strlen(tag)+1) >= MAXTAGNAMELEN)
+ return PMTRACE_ERR_TAGLENGTH;
+
+ __pmtimevalNow(&now);
+
+ /* give just enough info for comparison routine */
+ hash.tag = (char *)tag;
+ hash.taglength = len;
+ hash.id = _pmtraceid();
+ hash.tracetype = TRACE_TYPE_TRANSACT;
+
+ /* lock hash table for search and update then send data */
+ TRACE_LOCK;
+
+ if ((hptr = __pmhashlookup(&_pmtable, tag, &hash)) == NULL)
+ sts = PMTRACE_ERR_NOSUCHTAG;
+ else if (hptr->inprogress != 1)
+ sts = PMTRACE_ERR_NOPROGRESS;
+ else if (hptr->tracetype != TRACE_TYPE_TRANSACT)
+ sts = PMTRACE_ERR_TAGTYPE;
+ else {
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_API)
+ fprintf(stderr, "pmtraceend: sending transaction data '%s' "
+ "(id=0x%" PRIx64 ")\n", tag, hash.id);
+#endif
+ hptr->inprogress = 0;
+ hptr->data = __pmtracetvsub(&now, &hptr->start);
+
+ if (sts >= 0 && _pmtimedout) {
+ sts = _pmtracereconnect();
+ sts = _pmtraceremaperr(sts);
+ }
+
+ if (sts >= 0) {
+ sts = __pmtracesenddata(__pmfd, hptr->tag, hptr->taglength,
+ TRACE_TYPE_TRANSACT, hptr->data);
+ sts = _pmtraceremaperr(sts);
+ }
+
+ protocol = __pmtraceprotocol(TRACE_PROTOCOL_QUERY);
+
+ if (sts >= 0 && protocol == TRACE_PROTOCOL_SYNC)
+ sts = _pmtracegetack(sts, TRACE_TYPE_TRANSACT);
+
+ if (sts == PMTRACE_ERR_IPC && protocol == TRACE_PROTOCOL_SYNC) {
+ _pmtimedout = 1; /* try reconnect */
+ sts = 0;
+ }
+ }
+
+ if (TRACE_UNLOCK != 0)
+ return -oserror();
+
+ return sts;
+}
+
+int
+pmtraceabort(const char *tag)
+{
+ _pmTraceLibdata hash;
+ _pmTraceLibdata *hptr;
+ int len, sts = 0;
+
+ if (tag == NULL || *tag == '\0')
+ return PMTRACE_ERR_TAGNAME;
+ if ((len = strlen(tag)+1) >= MAXTAGNAMELEN)
+ return PMTRACE_ERR_TAGLENGTH;
+
+ hash.tag = (char *)tag;
+ hash.taglength = len;
+ hash.id = _pmtraceid();
+ hash.tracetype = TRACE_TYPE_TRANSACT;
+
+ /* lock hash table for search and update */
+ TRACE_LOCK;
+ if ((hptr = __pmhashlookup(&_pmtable, tag, &hash)) == NULL)
+ sts = PMTRACE_ERR_NOSUCHTAG;
+ else if (hptr->inprogress != 1)
+ sts = PMTRACE_ERR_NOPROGRESS;
+ else if (hptr->tracetype != TRACE_TYPE_TRANSACT)
+ sts = PMTRACE_ERR_TAGTYPE;
+ else {
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_API)
+ fprintf(stderr, "pmtraceabort: aborting transaction '%s' "
+ "(id=0x%" PRIx64 ")\n", tag, hash.id);
+#endif
+ hptr->inprogress = 0;
+ }
+
+ if (TRACE_UNLOCK != 0)
+ return -oserror();
+
+ return sts;
+}
+
+
+static int
+_pmtracecommon(const char *label, double value, int type)
+{
+ static int first = 1;
+ int taglength;
+ int protocol;
+ int sts = 0;
+
+ if (label == NULL || *label == '\0')
+ return PMTRACE_ERR_TAGNAME;
+
+ taglength = (unsigned int)strlen(label)+1;
+ if (taglength >= MAXTAGNAMELEN)
+ return PMTRACE_ERR_TAGLENGTH;
+
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_API)
+ fprintf(stderr, "_pmtracecommon: trace tag '%s' (type=%d,value=%f)\n",
+ label, type, value);
+#endif
+
+ protocol = __pmtraceprotocol(TRACE_PROTOCOL_QUERY);
+
+ if (_pmtimedout && (sts = _pmtraceconnect(1)) < 0) {
+ if (first || protocol == TRACE_PROTOCOL_ASYNC)
+ return sts;
+ sts = _pmtraceremaperr(sts);
+ if (sts == PMTRACE_ERR_IPC && protocol == TRACE_PROTOCOL_SYNC) {
+ _pmtimedout = 1; /* try reconnect */
+ sts = 0;
+ }
+ return sts;
+ }
+ first = 0;
+
+ TRACE_LOCK;
+
+ if (sts >= 0 && _pmtimedout) {
+ sts = _pmtracereconnect();
+ sts = _pmtraceremaperr(sts);
+ }
+
+ if (sts >= 0) {
+ sts = __pmtracesenddata(__pmfd, (char *)label, (int)strlen(label)+1,
+ type, value);
+ sts = _pmtraceremaperr(sts);
+ }
+
+ protocol = __pmtraceprotocol(TRACE_PROTOCOL_QUERY);
+
+ if (sts >= 0 && protocol == TRACE_PROTOCOL_SYNC)
+ sts = _pmtracegetack(sts, type);
+
+ if (sts == PMTRACE_ERR_IPC && protocol == TRACE_PROTOCOL_SYNC) {
+ _pmtimedout = 1;
+ sts = 0;
+ }
+
+ if (TRACE_UNLOCK != 0)
+ return -oserror();
+
+ return sts;
+}
+
+
+int
+pmtracepoint(const char *label)
+{
+ return _pmtracecommon(label, -1, TRACE_TYPE_POINT);
+}
+
+
+int
+pmtracecounter(const char *label, double value)
+{
+ return _pmtracecommon(label, value, TRACE_TYPE_COUNTER);
+}
+
+int
+pmtraceobs(const char *label, double value)
+{
+ return _pmtracecommon(label, value, TRACE_TYPE_OBSERVE);
+}
+
+
+char *
+pmtraceerrstr(int code)
+{
+ static const struct {
+ int code;
+ char *msg;
+ } errtab[] = {
+ { PMTRACE_ERR_TAGNAME,
+ "Invalid tag name - tag names cannot be NULL" },
+ { PMTRACE_ERR_INPROGRESS,
+ "Transaction is already in progress - cannot begin" },
+ { PMTRACE_ERR_NOPROGRESS,
+ "Transaction is not currently in progress - cannot end" },
+ { PMTRACE_ERR_NOSUCHTAG,
+ "Transaction tag was not successfully initialised" },
+ { PMTRACE_ERR_TAGTYPE,
+ "Tag is already in use for a different type of tracing" },
+ { PMTRACE_ERR_TAGLENGTH,
+ "Tag name is too long (maximum 256 characters)" },
+ { PMTRACE_ERR_IPC,
+ "IPC protocol failure" },
+ { PMTRACE_ERR_ENVFORMAT,
+ "Unrecognised environment variable format" },
+ { PMTRACE_ERR_TIMEOUT,
+ "Application timed out connecting to the PMDA" },
+ { PMTRACE_ERR_VERSION,
+ "Incompatible versions between application and PMDA" },
+ { PMTRACE_ERR_PERMISSION,
+ "Cannot connect to PMDA - permission denied" },
+ { PMTRACE_ERR_CONNLIMIT,
+ "Cannot connect to PMDA - connection limit reached" },
+ { 0, "" }
+ };
+
+ if ((code < 0) && (code > -PMTRACE_ERR_BASE))
+ /* intro(2) errors */
+ return strerror(-code);
+ else if (code == 0)
+ return "No error.";
+ else {
+ int i;
+ for (i=0; errtab[i].code; i++) {
+ if (errtab[i].code == code)
+ return errtab[i].msg;
+ }
+ }
+ return "Unknown error code.";
+}
+
+
+static int
+_pmtraceremaperr(int sts)
+{
+ int save_oserror;
+ int socket_closed;
+
+ /*
+ * sts is negative.
+ * Use __pmSocketClosed() to decode it, since it may have come from
+ * __pmSecureSocketsError(). __pmSocketClosed uses oserror() and expects it to
+ * be non-negative.
+ */
+ save_oserror = oserror();
+ setoserror(-sts);
+ socket_closed = __pmSocketClosed();
+ setoserror(save_oserror);
+
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_COMMS)
+ fprintf(stderr, "_pmtraceremaperr: status %d remapped to %d\n", sts,
+ socket_closed ? PMTRACE_ERR_IPC : sts);
+#endif
+
+ if (socket_closed) {
+ _pmtimedout = 1;
+ return PMTRACE_ERR_IPC;
+ }
+ return sts;
+}
+
+
+static __uint64_t
+_pmtraceid(void)
+{
+ __uint64_t myid = 0;
+ myid |= getpid();
+ return myid;
+}
+
+
+/*
+ * gets an ack from the PMDA, based on expected ACK type.
+ * if type is zero, expects and returns the version, otherwise
+ * ACK is for a sent data PDU.
+ */
+static int
+_pmtracegetack(int sts, int type)
+{
+ if (sts >= 0) {
+ __pmTracePDU *ack;
+ int status, acktype;
+
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_NOAGENT) {
+ fprintf(stderr, "_pmtracegetack: awaiting ack (skipped)\n");
+ return 0;
+ }
+ else if (__pmstate & PMTRACE_STATE_COMMS)
+ fprintf(stderr, "_pmtracegetack: awaiting ack ...\n");
+#endif
+
+ status = __pmtracegetPDU(__pmfd, TRACE_TIMEOUT_DEFAULT, &ack);
+
+ if (status < 0) {
+ if ((status = _pmtraceremaperr(status)) == PMTRACE_ERR_IPC)
+ return 0; /* hide this - try reconnect later */
+ return status;
+ }
+ else if (status == 0) {
+ _pmtimedout = 1;
+ return PMTRACE_ERR_IPC;
+ }
+ else if (status == TRACE_PDU_ACK) {
+ if ((status = __pmtracedecodeack(ack, &acktype)) < 0)
+ return _pmtraceremaperr(status);
+ else if (type != 0 && acktype == type)
+ return 0; /* alls well */
+ else if (acktype < 0)
+ return _pmtraceremaperr(acktype);
+ else if (type == 0)
+ /* acktype contains PDU version if needed (not currently) */
+ return acktype;
+ return PMTRACE_ERR_IPC;
+ }
+ else {
+ fprintf(stderr, "_pmtracegetack: unknown PDU type (0x%x)\n", status);
+ return PMTRACE_ERR_IPC;
+ }
+ }
+ return _pmtraceremaperr(sts);
+}
+
+
+static void
+_pmtraceupdatewait(void)
+{
+ static int defbackoff[] = {5, 10, 20, 40, 80};
+ static int *backoff = NULL;
+ static int n_backoff = 0;
+ char *q;
+
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_COMMS)
+ fprintf(stderr, "_pmtraceupdatewait: updating reconnect back-off\n");
+#endif
+ if (n_backoff == 0) { /* compute backoff before trying again */
+ if ((q = getenv(TRACE_ENV_RECTIMEOUT)) != NULL) {
+ char *pend, *p;
+ int val;
+
+ for (p=q; *p != '\0'; ) {
+ val = (int)strtol(p, &pend, 10);
+ if (val <= 0 || (*pend != ',' && *pend != '\0')) {
+ n_backoff = 0;
+ if (backoff != NULL)
+ free(backoff);
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_COMMS)
+ fprintf(stderr, "_pmtraceupdatewait: bad reconnect "
+ "format in %s.\n", TRACE_ENV_RECTIMEOUT);
+#endif
+ break;
+ }
+ if ((backoff = (int *)realloc(backoff, n_backoff *
+ sizeof(backoff[0]))) == NULL)
+ break;
+ backoff[n_backoff++] = val;
+ if (*pend == '\0')
+ break;
+ p = &pend[1];
+ }
+ }
+ if (n_backoff == 0) { /* use defaults */
+ n_backoff = 5;
+ backoff = defbackoff;
+ }
+ }
+ if (_pmtimedout == 0)
+ _pmtimedout = 1;
+ else if (_pmtimedout < n_backoff)
+ _pmtimedout++;
+ _pmttimeout = time(NULL) + backoff[_pmtimedout-1];
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_COMMS)
+ fprintf(stderr, "_pmtraceupdatewait: next attempt after %d seconds\n",
+ backoff[_pmtimedout-1]);
+#endif
+}
+
+
+static int
+_pmtracereconnect(void)
+{
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_NOAGENT) {
+ fprintf(stderr, "_pmtracereconnect: reconnect attempt (skipped)\n");
+ return 0;
+ }
+ else if (__pmstate & PMTRACE_STATE_COMMS) {
+ fprintf(stderr, "_pmtracereconnect: attempting PMDA reconnection\n");
+ }
+#endif
+
+ if (_pmtimedout && time(NULL) < _pmttimeout) { /* too soon to retry */
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_COMMS)
+ fprintf(stderr, "_pmtracereconnect: too soon to retry "
+ "(%d seconds remain)\n", (int)(_pmttimeout - time(NULL)));
+#endif
+ return -ETIMEDOUT;
+ }
+ if (__pmfd >= 0) {
+ __pmtracenomoreinput(__pmfd);
+ __pmCloseSocket(__pmfd);
+ __pmfd = -1;
+ }
+ if (_pmtraceconnect(1) < 0) {
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_COMMS)
+ fprintf(stderr, "_pmtracereconnect: failed to reconnect\n");
+#endif
+ _pmtraceupdatewait();
+ return -ETIMEDOUT;
+ }
+ else {
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_COMMS)
+ fprintf(stderr, "_pmtracereconnect: reconnect succeeded!\n");
+#endif
+ _pmtimedout = 0;
+ }
+ return 0;
+}
+
+#ifndef IS_MINGW
+static struct itimerval _pmmyitimer, off_itimer;
+static void _pmtracealarm(int dummy) { }
+static void _pmtraceinit(void) { }
+#else
+static void _pmtraceinit(void)
+{
+ WORD wVersionRequested = MAKEWORD(2, 2);
+ WSADATA wsaData;
+ WSAStartup(wVersionRequested, &wsaData);
+ _fmode = O_BINARY;
+}
+#endif
+
+static int
+_pmtraceconnect(int doit)
+{
+ static int first = 1;
+ int sts = 0;
+
+ if (!_pmtimedout)
+ return 0;
+ else if (first) { /* once-off, not to be done on reconnect */
+ _pmtraceinit();
+ if (TRACE_LOCK_INIT < 0)
+ return -oserror();
+ first = 0;
+ TRACE_LOCK;
+ sts = __pmhashinit(&_pmtable, 0, sizeof(_pmTraceLibdata),
+ _pmlibcmp, _pmlibdel);
+ if (TRACE_UNLOCK != 0)
+ return -oserror();
+ }
+ else if (__pmtraceprotocol(TRACE_PROTOCOL_QUERY) == TRACE_PROTOCOL_ASYNC)
+ return PMTRACE_ERR_IPC;
+
+ if (sts >= 0 && doit)
+ sts = _pmauxtraceconnect();
+ if (sts >= 0)
+ __pmtraceprotocol(TRACE_PROTOCOL_FINAL);
+
+ return sts;
+}
+
+static int
+_pmauxtraceconnect(void)
+{
+ int port = TRACE_PORT;
+ char hostname[MAXHOSTNAMELEN];
+ struct timeval timeout = { 3, 0 }; /* default 3 secs */
+ __pmSockAddr *myaddr;
+ __pmHostEnt *servinfo;
+ void *enumIx;
+#ifndef IS_MINGW
+ struct itimerval _pmolditimer;
+ void (*old_handler)(int foo);
+#endif
+ int rc, sts;
+ int flags = 0;
+ char *sptr, *endptr, *endnum;
+ struct timeval canwait = { 5, 000000 };
+ struct timeval stv;
+ struct timeval *pstv;
+ __pmFdSet wfds;
+
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_NOAGENT) {
+ fprintf(stderr, "_pmtraceconnect: connecting to PMDA (skipped)\n");
+ return 0;
+ }
+ else if (__pmstate & PMTRACE_STATE_COMMS)
+ fprintf(stderr, "_pmtraceconnect: connecting to PMDA ...\n");
+#endif
+
+ /*
+ * get optional stuff from environment ...
+ * PCP_TRACE_HOST, PCP_TRACE_PORT, PCP_TRACE_TIMEOUT, and
+ * PCP_TRACE_NOAGENT
+ */
+ if ((sptr = getenv(TRACE_ENV_HOST)) != NULL)
+ strcpy(hostname, sptr);
+ else {
+ (void)gethostname(hostname, MAXHOSTNAMELEN);
+ hostname[MAXHOSTNAMELEN-1] = '\0';
+ }
+ if ((sptr = getenv(TRACE_ENV_PORT)) != NULL) {
+ port = (int)strtol(sptr, &endnum, 0);
+ if (*endnum != '\0' || port < 0) {
+ fprintf(stderr, "trace warning: bad PCP_TRACE_PORT ignored.");
+ port = TRACE_PORT;
+ }
+ }
+ if ((sptr = getenv(TRACE_ENV_TIMEOUT)) != NULL) {
+ double timesec = strtod(sptr, &endptr);
+ if (*endptr != '\0' || timesec < 0.0)
+ fprintf(stderr, "trace warning: bogus PCP_TRACE_TIMEOUT.");
+ else {
+ timeout.tv_sec = (time_t)timesec;
+ timeout.tv_usec = (int)((timesec - (double)timeout.tv_sec)*1000000);
+ }
+ }
+ if (getenv(TRACE_ENV_NOAGENT) != NULL)
+ __pmstate |= PMTRACE_STATE_NOAGENT;
+
+ if ((servinfo = __pmGetAddrInfo(hostname)) == NULL) {
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_COMMS)
+ fprintf(stderr, "_pmtraceconnect(__pmGetAddrInfo(hostname=%s): "
+ "hosterror=%d, ``%s''\n", hostname, hosterror(),
+ hoststrerror());
+#endif
+ return -EHOSTUNREACH;
+ }
+
+ /* Try each address in turn until one connects. */
+ sts = EHOSTUNREACH;
+ __pmfd = -1;
+ enumIx = NULL;
+ for (myaddr = __pmHostEntGetSockAddr(servinfo, &enumIx);
+ myaddr != NULL;
+ myaddr = __pmHostEntGetSockAddr(servinfo, &enumIx)) {
+ /* Create a socket */
+ if (__pmSockAddrIsInet(myaddr))
+ __pmfd = __pmCreateSocket();
+ else if (__pmSockAddrIsIPv6(myaddr))
+ __pmfd = __pmCreateIPv6Socket();
+ else {
+ fprintf(stderr, "_pmtraceconnect(invalid address family): %d\n",
+ __pmSockAddrGetFamily(myaddr));
+ }
+ if (__pmfd < 0) {
+ sts = neterror();
+ __pmSockAddrFree(myaddr);
+ continue; /* Try the next address */
+ }
+
+ /* Set the port. */
+ __pmSockAddrSetPort(myaddr, port);
+
+#ifndef IS_MINGW
+ /* arm interval timer */
+ _pmmyitimer.it_value.tv_sec = timeout.tv_sec;
+ _pmmyitimer.it_value.tv_usec = timeout.tv_usec;
+ _pmmyitimer.it_interval.tv_sec = 0;
+ _pmmyitimer.it_interval.tv_usec = 0;
+ old_handler = signal(SIGALRM, _pmtracealarm);
+ setitimer(ITIMER_REAL, &_pmmyitimer, &_pmolditimer);
+#endif
+
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_COMMS) {
+ char *name = __pmHostEntGetName (servinfo);
+ fprintf(stderr, "_pmtraceconnect: PMDA host=%s port=%d timeout=%d"
+ "secs\n", name == NULL ? "unknown" : name, port, (int)timeout.tv_sec);
+ if (name != NULL)
+ free(name);
+ }
+#endif
+
+ /* Attempt to connect */
+ flags = __pmConnectTo(__pmfd, myaddr, port);
+ __pmSockAddrFree(myaddr);
+
+ if (flags < 0) {
+ /*
+ * Mark failure in case we fall out the end of the loop
+ * and try next address. __pmfd has been closed in __pmConnectTo().
+ */
+ sts = -flags;
+ __pmfd = -1;
+ continue;
+ }
+
+ /* FNDELAY and we're in progress - wait on select */
+ stv = canwait;
+ pstv = (stv.tv_sec || stv.tv_usec) ? &stv : NULL;
+ __pmFD_ZERO(&wfds);
+ __pmFD_SET(__pmfd, &wfds);
+ if ((rc = __pmSelectWrite(__pmfd+1, &wfds, pstv)) == 1) {
+ sts = __pmConnectCheckError(__pmfd);
+ }
+ else if (rc == 0) {
+ sts = ETIMEDOUT;
+ }
+ else {
+ sts = (rc < 0) ? neterror() : EINVAL;
+ }
+
+#ifndef IS_MINGW
+ /* re-arm interval timer */
+ setitimer(ITIMER_REAL, &off_itimer, &_pmmyitimer);
+ signal(SIGALRM, old_handler);
+ if (_pmolditimer.it_value.tv_sec != 0 && _pmolditimer.it_value.tv_usec != 0) {
+ _pmolditimer.it_value.tv_usec -= timeout.tv_usec - _pmmyitimer.it_value.tv_usec;
+ while (_pmolditimer.it_value.tv_usec < 0) {
+ _pmolditimer.it_value.tv_usec += 1000000;
+ _pmolditimer.it_value.tv_sec--;
+ }
+ while (_pmolditimer.it_value.tv_usec > 1000000) {
+ _pmolditimer.it_value.tv_usec -= 1000000;
+ _pmolditimer.it_value.tv_sec++;
+ }
+ _pmolditimer.it_value.tv_sec -= timeout.tv_sec - _pmmyitimer.it_value.tv_sec;
+ if (_pmolditimer.it_value.tv_sec < 0) {
+ /* missed the user's itimer, pretend there is 1 msec to go! */
+ _pmolditimer.it_value.tv_sec = 0;
+ _pmolditimer.it_value.tv_usec = 1000;
+ }
+ setitimer(ITIMER_REAL, &_pmolditimer, &_pmmyitimer);
+ }
+#endif
+
+ /* Was the connection successful? */
+ if (sts == 0)
+ break;
+
+ /* Unsuccessful connection. */
+ __pmCloseSocket(__pmfd);
+ __pmfd = -1;
+ } /* loop over addresses */
+
+ __pmHostEntFree(servinfo);
+
+ /* Was the connection successful? */
+ if (__pmfd < 0) {
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_COMMS)
+ fprintf(stderr, "_pmtraceconnect(socket failed): %s\n",
+ netstrerror());
+#endif
+ return -sts;
+ }
+
+ _pmtimedout = 0;
+
+ /* Restore the original file status flags. */
+ if (__pmSetFileStatusFlags(__pmfd, flags) < 0) {
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_COMMS)
+ fprintf(stderr, ":_pmtraceconnect: cannot restore file status flags\n");
+#endif
+ return -oserror();
+ }
+
+ /* make sure this file descriptor is closed if exec() is called */
+ if ((flags = __pmGetFileDescriptorFlags(__pmfd)) != -1)
+ sts = __pmSetFileDescriptorFlags(__pmfd, flags | FD_CLOEXEC);
+ else
+ sts = -1;
+ if (sts == -1)
+ return -oserror();
+
+ if (__pmtraceprotocol(TRACE_PROTOCOL_QUERY) == TRACE_PROTOCOL_ASYNC) {
+ /* in the asynchronoous protocol - ensure no delay after close */
+ if ((flags = __pmGetFileStatusFlags(__pmfd)) != -1)
+ sts = __pmSetFileStatusFlags(__pmfd, flags | FNDELAY);
+ else
+ sts = -1;
+ if (sts == -1)
+ return -oserror();
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_COMMS)
+ fprintf(stderr, "_pmtraceconnect: async protocol setup complete\n");
+#endif
+ }
+ else
+#ifdef PMTRACE_DEBUG
+ if (__pmstate & PMTRACE_STATE_COMMS)
+ fprintf(stderr, "_pmtraceconnect: sync protocol setup complete\n");
+#endif
+
+ /* trace PMDA sends an ACK on successful connect */
+ sts = _pmtracegetack(sts, 0);
+
+ return sts;
+}
+
+
+/* this is used internally to maintain connection state */
+int
+__pmtraceprotocol(int update)
+{
+ static int fixedinstone = 0;
+ static int protocol = TRACE_PROTOCOL_SYNC;
+
+ if (update == TRACE_PROTOCOL_QUERY) /* just a state request */
+ return protocol;
+ else if (update == TRACE_PROTOCOL_FINAL) /* no more changes allowed */
+ fixedinstone = 1;
+ else if (!fixedinstone && update == TRACE_PROTOCOL_ASYNC) {
+ __pmstate |= PMTRACE_STATE_ASYNC;
+ protocol = update;
+ }
+
+ return protocol;
+}
+
+int
+pmtracestate(int code)
+{
+ int old = __pmstate;
+
+ if (code & PMTRACE_STATE_ASYNC) {
+ if (__pmtraceprotocol(TRACE_PROTOCOL_ASYNC) != TRACE_PROTOCOL_ASYNC)
+ /* only can do this before connection established */
+ return -EINVAL;
+ }
+
+ __pmstate = code;
+ return old;
+}