diff options
Diffstat (limited to 'src/libpcp_trace')
-rw-r--r-- | src/libpcp_trace/GNUmakefile | 28 | ||||
-rw-r--r-- | src/libpcp_trace/src/GNUmakefile | 74 | ||||
-rw-r--r-- | src/libpcp_trace/src/exports | 42 | ||||
-rw-r--r-- | src/libpcp_trace/src/ftrace.c | 134 | ||||
-rw-r--r-- | src/libpcp_trace/src/hash.c | 181 | ||||
-rw-r--r-- | src/libpcp_trace/src/hash.h | 54 | ||||
-rw-r--r-- | src/libpcp_trace/src/p_ack.c | 71 | ||||
-rw-r--r-- | src/libpcp_trace/src/p_data.c | 179 | ||||
-rw-r--r-- | src/libpcp_trace/src/pdu.c | 419 | ||||
-rw-r--r-- | src/libpcp_trace/src/pdubuf.c | 182 | ||||
-rw-r--r-- | src/libpcp_trace/src/trace.c | 950 |
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; +} |