summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Coopersmith <alan.coopersmith@oracle.com>2012-07-21 23:42:23 -0700
committerAlan Coopersmith <alan.coopersmith@oracle.com>2012-10-02 22:16:08 -0700
commit8b26ded773e1cd4890e93836692ecc9f136c5efd (patch)
tree11a54c0a6748a834d6483f19432e58e7566e5f57
parent5cf76eb8f489f6c4d1ba76bf6fe782c3cf45803d (diff)
downloadxscope-8b26ded773e1cd4890e93836692ecc9f136c5efd.tar.gz
Add experimental support for reading a previously recorded file
If you run "xscope -r -v0 > xscope.raw", then you can later run "xscope -f xscope.raw" to decode the data. Mainly adding this to aid in testing decoding of new extensions, so I don't have to re-run the commands over and over, just replay a log file. (Which may or may not have been edited to include additional request data for further testing of types & formats that clients may not make it easy to generate.) Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com>
-rw-r--r--man/xscope.man5
-rw-r--r--scope.c330
2 files changed, 331 insertions, 4 deletions
diff --git a/man/xscope.man b/man/xscope.man
index d97790a..f90a2f2 100644
--- a/man/xscope.man
+++ b/man/xscope.man
@@ -58,6 +58,11 @@ Defines the display number. The display number is added to the input
and output port to give the actual ports which are used by \fIxscope\fP.
.PP
.TP 10
+.BI \-f <filename>
+Instead of decoding a live session, reads previously captured raw data
+from a file to decode.
+.PP
+.TP 10
.B \-q
Quiet output mode. Gives only the names of requests, replies, errors, and
events, but does not indicate contents.
diff --git a/scope.c b/scope.c
index 1234f82..d3aa86f 100644
--- a/scope.c
+++ b/scope.c
@@ -25,7 +25,7 @@
*
*/
/*
- * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -86,10 +86,12 @@ long ServerBasePort = DefaultPort;
static long ServerInPort = 1;
static long ServerOutPort = 0;
static long ServerDisplay = 0;
+static char *RawFile;
static FD ConnectToClient(FD ConnectionSocket);
static void DataFromClient(FD fd);
static void SetUpStdin(void);
+static void SetUpRawFile(const char *filename);
char TranslateText = 0;
char IsUTF8locale = 0;
@@ -768,6 +770,16 @@ ScanArgs(int argc, char **argv)
debug(1, (stderr, "ServerHostName = %s\n", ServerHostName));
break;
+ case 'f':
+ if (++*argv != NULL && **argv != '\0')
+ RawFile = *argv;
+ else if (argv[1] != NULL) {
+ RawFile = argv[1];
+ argv++; argc--;
+ }
+ debug(1, (stderr, "RawFile = %s\n", RawFile));
+ break;
+
case 'T':
TranslateText = 1;
break;
@@ -828,9 +840,13 @@ main(int argc, char **argv)
if (DoAudio)
InitializeAudio();
SetUpStdin();
- SetUpConnectionSocket(GetScopePort(), NewConnection);
- if (DoAudio)
- SetUpConnectionSocket(GetScopePort() + 2000, NewAudio);
+ if (RawFile) {
+ SetUpRawFile(RawFile);
+ } else {
+ SetUpConnectionSocket(GetScopePort(), NewConnection);
+ if (DoAudio)
+ SetUpConnectionSocket(GetScopePort() + 2000, NewAudio);
+ }
SetSignalHandling();
return MainLoop();
@@ -1255,3 +1271,309 @@ ConnectToServer(Boolean report)
}
return (ServerFD);
}
+
+
+/* ************************************************************ */
+/* */
+/* Read & decode data from a pre-recorded raw file (-r -v0) */
+/* */
+/* ************************************************************ */
+
+static FILE *raw;
+static FD *fdMap; /* map of fds from file to current runtime */
+static int fdMapSize;
+
+static void
+SetUpRawClient(FD *returnClientFD, FD *returnServerFD)
+{
+ FD ClientFD, ServerFD;
+
+ /* need to fake a pair of fd's */
+ ClientFD = open("/dev/null", O_RDONLY);
+ if (ClientFD < 1) {
+ perror("/dev/null");
+ panic("Can't open /dev/null for reading");
+ }
+ ServerFD = dup(ClientFD);
+ if (ServerFD < 1) {
+ perror("ClientFD");
+ panic("Can't dup ClientFD for reading");
+ }
+ UsingFD(ClientFD, NULL, NULL, NULL);
+ UsingFD(ServerFD, NULL, NULL, NULL);
+ SetUpPair(ClientFD, ServerFD);
+ StartClientConnection(ClientFD);
+ StartServerConnection(ServerFD);
+ *returnClientFD = ClientFD;
+ *returnServerFD = ServerFD;
+}
+
+static inline FD
+GetRawFDMap(FD origFD)
+{
+ if (origFD >= fdMapSize)
+ return -1;
+ return fdMap[origFD];
+}
+
+static void
+SetRawFDMap(FD origFD, FD currentFD)
+{
+ if (origFD >= fdMapSize) {
+ int newSize = fdMapSize + 32;
+ if (origFD >= newSize)
+ newSize = origFD;
+ fdMap = realloc(fdMap, newSize);
+ if (fdMap == NULL)
+ panic("Can't allocate memory for fdMap");
+ memset(fdMap + fdMapSize, -1, newSize - fdMapSize);
+ fdMapSize = newSize;
+ }
+ fdMap[origFD] = currentFD;
+ debug(16, (stderr, "FD Map %d => %d\n", origFD, currentFD));
+}
+
+static void
+UnmapRawFD(FD currentFD)
+{
+ FD fd;
+
+ for (fd = 0; fd < fdMapSize; fd++) {
+ if (fdMap[fd] == currentFD) {
+ debug(16, (stderr, "Clearing FD Map %d => %d\n", fd, currentFD));
+ fdMap[fd] = -1;
+ return;
+ }
+ }
+}
+
+
+static void
+DataFromRawFile(FD rawfd)
+{
+ Boolean fromClient = true;
+ FD fd = -1;
+ char *in, *n;
+ unsigned char *out;
+ static FD newServerFD;
+
+ enterprocedure("DataFromRawFile");
+
+ Verbose = XVerbose;
+
+ for (;;) {
+
+ /* If we already read a line last time, process it first,
+ else get the next line for processing */
+ if (FDinfo[rawfd].bufcount == 0) {
+ if (fgets(FDinfo[rawfd].buffer, BUFFER_SIZE, raw) == NULL) {
+ CloseConnection(rawfd);
+ exit(0);
+ }
+ FDinfo[rawfd].bufcount = strlen(FDinfo[rawfd].buffer);
+ debug(16, (stderr, "raw input = %s", FDinfo[rawfd].buffer));
+ }
+
+ in = FDinfo[rawfd].buffer;
+
+ /* lines starting with space indicate change of sender */
+ if (isspace(*in)) {
+ /* If we already have data in buffer, process it */
+ if ((fd != -1) && (FDinfo[fd].bufcount > 0)) {
+ debug(16, (stderr, "reporting %d bytes from: %s %s\n",
+ FDinfo[fd].bufcount,
+ fromClient ? "client" : "server",
+ ClientName(fd)));
+ if (fromClient)
+ ReportFromClient(fd, FDinfo[fd].buffer,
+ FDinfo[fd].bufcount);
+ else
+ ReportFromServer(fd, FDinfo[fd].buffer,
+ FDinfo[fd].bufcount);
+ FDinfo[fd].bufcount = 0;
+ return;
+ }
+ else {
+ /* Need to parse header to figure out which direction */
+ while (isspace(*in)) {
+ in++;
+ }
+ if (strncmp(in, "Client Connect (fd ", 19) == 0) {
+ /* New client connection */
+ FD ClientFD, ServerFD, origFD;
+
+ origFD = strtol(in + 19, NULL, 10);
+
+ SetUpRawClient(&ClientFD, &ServerFD);
+ SetRawFDMap(origFD, ClientFD);
+ fd = ClientFD;
+ fromClient = true;
+ newServerFD = ServerFD;
+ }
+ else if (strncmp(in, "Server Connect (fd ", 19) == 0) {
+ /* New client connection */
+ FD origFD;
+
+ if (newServerFD == 0)
+ panic("Server connection without matching client");
+
+ origFD = strtol(in + 19, NULL, 10);
+ SetRawFDMap(origFD, newServerFD);
+ fd = newServerFD;
+ fromClient = false;
+ newServerFD = 0;
+ }
+ else if (strstr(in, " --> EOF")) {
+ /* Closing client connection */
+ n = strstr(in, ": Client");
+ if (n != NULL) {
+ int clientid;
+
+ in = n + 8;
+ if (isdigit(*in))
+ clientid = strtol(in, NULL, 10);
+ else
+ clientid = 1;
+
+ for (fd = 0; fd < HighestFD; fd++) {
+ if (FDinfo[fd].ClientNumber == clientid) {
+ if (Verbose >= 0) {
+ PrintTime();
+ fprintf(stdout, "Client%s --> EOF\n",
+ ClientName(fd));
+ }
+ UnmapRawFD(fd);
+ UnmapRawFD(FDPair(fd));
+ CloseConnection(fd);
+ break;
+ }
+ }
+ }
+ fd = -1;
+ }
+ else {
+ FD origFD;
+
+ if ((strncmp(in, "Request ", 8) == 0))
+ fromClient = true;
+ else
+ fromClient = false;
+
+ in = strstr(in, "(fd ");
+ if (in == NULL) {
+ warn("Did not find fd string in input entry");
+ warn(FDinfo[rawfd].buffer);
+ FDinfo[rawfd].bufcount = 0;
+ continue;
+ }
+
+ origFD = strtol(in + 4, NULL, 10);
+ fd = GetRawFDMap(origFD);
+ if (fd == -1) {
+ debug(16, (stderr, "origFD = %d\n", origFD));
+ warn("Unknown fd in input entry");
+ FDinfo[rawfd].bufcount = 0;
+ continue;
+ }
+ }
+ debug(16, (stderr, "raw data from: %s\n",
+ fromClient ? "client" : "server"));
+ /* skip over rest of header */
+ n = strchr(in, ':');
+ if (n != NULL)
+ in = n + 1;
+ while (isspace(*in))
+ in++;
+ }
+ }
+
+ if (fd == -1) {
+ /* dump data we don't know what to do with */
+ FDinfo[rawfd].bufcount = 0;
+ continue;
+ }
+
+ out = FDinfo[fd].buffer + FDinfo[fd].bufcount;
+
+ while (*in != '\0') {
+ unsigned char val;
+
+ if ((in[0] >= '0') && in[0] <= '9') {
+ val = (in[0] - '0') << 4;
+ }
+ else if ((in[0] >= 'a') && in[0] <= 'f') {
+ val = (in[0] - 'a' + 0x0a) << 4;
+ }
+ else {
+ warn(in);
+ warn("invalid raw file input");
+ break;
+ }
+
+ if ((in[1] >= '0') && in[1] <= '9') {
+ val += (in[1] - '0');
+ }
+ else if ((in[1] >= 'a') && in[1] <= 'f') {
+ val += (in[1] - 'a' + 0x0a);
+ }
+ else {
+ warn(in + 1);
+ warn("invalid raw file input");
+ break;
+ }
+
+ if (in[2] == ' ') {
+ in += 3;
+ } else {
+ in += 2;
+ }
+ while ((*in == '\r') || (*in == '\n'))
+ in++;
+
+ *out++ = val;
+ FDinfo[fd].bufcount++;
+
+ /* If buffer is full, process what we have now */
+ if (FDinfo[fd].bufcount >= BUFFER_SIZE) {
+ if (fromClient)
+ ReportFromClient(fd, FDinfo[fd].buffer,
+ FDinfo[fd].bufcount);
+ else
+ ReportFromServer(fd, FDinfo[fd].buffer,
+ FDinfo[fd].bufcount);
+ FDinfo[fd].bufcount = 0;
+ out = FDinfo[fd].buffer;
+ }
+ }
+ FDinfo[rawfd].bufcount = 0;
+ }
+}
+
+static void
+SetUpRawFile(const char *filename)
+{
+ FD fd;
+ enterprocedure("SetUpRawFile");
+
+ if (strcmp(filename, "-") == 0)
+ raw = stdin;
+ else
+ raw = fopen(filename, "r");
+ if (raw == NULL) {
+ perror(filename);
+ panic("Can't open raw file for reading");
+ }
+ fd = fileno(raw);
+ debug(4, (stderr, "Opened raw file %s: FD %d\n", filename, fd));
+
+ if (FDinfo[fd].buffer == NULL) {
+ FDinfo[fd].buffer = calloc(1, BUFFER_SIZE);
+ if (FDinfo[fd].buffer == NULL)
+ panic("unable to allocate client buffer");
+ }
+ FDinfo[fd].bufcount = 0;
+ FDinfo[fd].buflimit = -1;
+ FDinfo[fd].bufdelivered = 0;
+
+ UsingFD(fd, DataFromRawFile, NULL, NULL);
+}