HPCToolkit
server.cpp
Go to the documentation of this file.
1 // -*-Mode: C++;-*- // technically C99
2 
3 // * BeginRiceCopyright *****************************************************
4 //
5 // $HeadURL$
6 // $Id$
7 //
8 // --------------------------------------------------------------------------
9 // Part of HPCToolkit (hpctoolkit.org)
10 //
11 // Information about sources of support for research and development of
12 // HPCToolkit is at 'hpctoolkit.org' and in 'README.Acknowledgments'.
13 // --------------------------------------------------------------------------
14 //
15 // Copyright ((c)) 2002-2019, Rice University
16 // All rights reserved.
17 //
18 // Redistribution and use in source and binary forms, with or without
19 // modification, are permitted provided that the following conditions are
20 // met:
21 //
22 // * Redistributions of source code must retain the above copyright
23 // notice, this list of conditions and the following disclaimer.
24 //
25 // * Redistributions in binary form must reproduce the above copyright
26 // notice, this list of conditions and the following disclaimer in the
27 // documentation and/or other materials provided with the distribution.
28 //
29 // * Neither the name of Rice University (RICE) nor the names of its
30 // contributors may be used to endorse or promote products derived from
31 // this software without specific prior written permission.
32 //
33 // This software is provided by RICE and contributors "as is" and any
34 // express or implied warranties, including, but not limited to, the
35 // implied warranties of merchantability and fitness for a particular
36 // purpose are disclaimed. In no event shall RICE or contributors be
37 // liable for any direct, indirect, incidental, special, exemplary, or
38 // consequential damages (including, but not limited to, procurement of
39 // substitute goods or services; loss of use, data, or profits; or
40 // business interruption) however caused and on any theory of liability,
41 // whether in contract, strict liability, or tort (including negligence
42 // or otherwise) arising in any way out of the use of this software, even
43 // if advised of the possibility of such damage.
44 //
45 // ******************************************************* EndRiceCopyright *
46 
47 // The server side of the new fnbounds server. Hpcrun creates a pipe
48 // and then forks and execs hpcfnbounds in server mode (-s). The file
49 // descriptors are passed as command-line arguments.
50 //
51 // This file implements the server side of the pipe. Read messages
52 // over the pipe, process fnbounds queries and write the answer
53 // (including the array of addresses) back over the pipe. The file
54 // 'syserv-mesg.h' defines the API for messages over the pipe.
55 //
56 // Notes:
57 // 1. The server only computes fnbounds queries, not general calls to
58 // system(), use monitor_real_system() for that.
59 //
60 // 2. Catch SIGPIPE. Writing to a pipe after the other side has
61 // exited triggers a SIGPIPE and terminates the process. If this
62 // happens, it probably means that hpcrun has prematurely exited.
63 // So, catch SIGPIPE in order to write a more useful error message.
64 //
65 // 3. It's ok to write error messages to stderr. After hpcrun forks,
66 // it dups the hpcrun log file fd onto stdout and stderr so that any
67 // output goes to the log file.
68 //
69 // 4. The server runs outside of hpcrun and libmonitor.
70 //
71 // Todo:
72 // 1. The memory leak is fixed in symtab 8.0.
73 
74 //***************************************************************************
75 
76 #include <sys/types.h>
77 #include <sys/time.h>
78 #include <sys/resource.h>
79 #include <err.h>
80 #include <errno.h>
81 #include <setjmp.h>
82 #include <signal.h>
83 #include <stdint.h>
84 #include <stdio.h>
85 #include <stdlib.h>
86 #include <string.h>
87 #include <unistd.h>
88 
89 #include "code-ranges.h"
90 #include "function-entries.h"
91 #include "variable-entries.h"
92 
93 #include "process-ranges.h"
94 #include "server.h"
95 #include "syserv-mesg.h"
96 
97 #define ADDR_SIZE (256 * 1024)
98 #define INIT_INBUF_SIZE 2000
99 
100 #define SUCCESS 0
101 #define FAILURE -1
102 #define END_OF_FILE -2
103 
104 static int fdin;
105 static int fdout;
106 
107 static void *addr_buf[ADDR_SIZE];
108 static long num_addrs;
109 static long total_num_addrs;
110 static long max_num_addrs;
111 
112 static char *inbuf;
113 static long inbuf_size;
114 
116 
117 static int jmpbuf_ok = 0;
118 static sigjmp_buf jmpbuf;
119 
120 static int sent_ok_mesg;
121 
122 
123 //*****************************************************************
124 // I/O helper functions
125 //*****************************************************************
126 
127 // Automatically restart short reads over a pipe.
128 // Returns: SUCCESS, FAILURE or END_OF_FILE.
129 //
130 static int
131 read_all(int fd, void *buf, size_t count)
132 {
133  ssize_t ret;
134  size_t len;
135 
136  len = 0;
137  while (len < count) {
138  ret = read(fd, ((char *) buf) + len, count - len);
139  if (ret < 0 && errno != EINTR) {
140  return FAILURE;
141  }
142  if (ret == 0) {
143  return END_OF_FILE;
144  }
145  if (ret > 0) {
146  len += ret;
147  }
148  }
149 
150  return SUCCESS;
151 }
152 
153 
154 // Automatically restart short writes over a pipe.
155 // Returns: SUCCESS or FAILURE.
156 //
157 static int
158 write_all(int fd, const void *buf, size_t count)
159 {
160  ssize_t ret;
161  size_t len;
162 
163  len = 0;
164  while (len < count) {
165  ret = write(fd, ((const char *) buf) + len, count - len);
166  if (ret < 0 && errno != EINTR) {
167  return FAILURE;
168  }
169  if (ret > 0) {
170  len += ret;
171  }
172  }
173 
174  return SUCCESS;
175 }
176 
177 
178 // Read a single syserv mesg from incoming pipe.
179 // Returns: SUCCESS, FAILURE or END_OF_FILE.
180 //
181 static int
182 read_mesg(struct syserv_mesg *mesg)
183 {
184  int ret;
185 
186  memset(mesg, 0, sizeof(*mesg));
187  ret = read_all(fdin, mesg, sizeof(*mesg));
188  if (ret == SUCCESS && mesg->magic != SYSERV_MAGIC) {
189  ret = FAILURE;
190  }
191 
192  return ret;
193 }
194 
195 
196 // Write a single syserv mesg to outgoing pipe.
197 // Returns: SUCCESS or FAILURE.
198 //
199 static int
200 write_mesg(int32_t type, int64_t len)
201 {
202  struct syserv_mesg mesg;
203 
204  mesg.magic = SYSERV_MAGIC;
205  mesg.type = type;
206  mesg.len = len;
207 
208  return write_all(fdout, &mesg, sizeof(mesg));
209 }
210 
211 
212 //*****************************************************************
213 // callback functions
214 //*****************************************************************
215 
216 // Called from dump_function_entry().
217 void
218 syserv_add_addr(void *addr, long func_entry_map_size)
219 {
220  int ret;
221 
222  // send the OK mesg on first addr callback
223  if (! sent_ok_mesg) {
224  max_num_addrs = func_entry_map_size + 1;
226  if (ret != SUCCESS) {
227  errx(1, "write to fdout failed");
228  }
229  sent_ok_mesg = 1;
230  }
231 
232  // see if buffer needs to be flushed
233  if (num_addrs >= ADDR_SIZE) {
234  ret = write_all(fdout, addr_buf, num_addrs * sizeof(void *));
235  if (ret != SUCCESS) {
236  errx(1, "write to fdout failed");
237  }
238  num_addrs = 0;
239  }
240 
242  num_addrs++;
243  total_num_addrs++;
244 }
245 
246 
247 // Called from dump_header_info().
248 void
249 syserv_add_header(int is_relocatable, uintptr_t ref_offset)
250 {
251  fnb_info.is_relocatable = is_relocatable;
252  fnb_info.reference_offset = ref_offset;
253 }
254 
255 
256 //*****************************************************************
257 // signal handlers
258 //*****************************************************************
259 
260 static void
262 {
263  // SIGPIPE means that hpcrun has exited, probably prematurely.
264  if (sig == SIGPIPE) {
265  errx(0, "hpcrun has prematurely exited");
266  }
267 
268  // The other signals indicate an internal error.
269  if (jmpbuf_ok) {
270  siglongjmp(jmpbuf, 1);
271  }
272  errx(1, "got signal outside sigsetjmp: %d", sig);
273 }
274 
275 
276 // Catch segfaults, abort and SIGPIPE.
277 static void
279 {
280  struct sigaction act;
281 
282  act.sa_handler = signal_handler;
283  act.sa_flags = 0;
284  sigemptyset(&act.sa_mask);
285 
286  if (sigaction(SIGSEGV, &act, NULL) != 0) {
287  err(1, "sigaction failed on SIGSEGV");
288  }
289  if (sigaction(SIGBUS, &act, NULL) != 0) {
290  err(1, "sigaction failed on SIGBUS");
291  }
292  if (sigaction(SIGABRT, &act, NULL) != 0) {
293  err(1, "sigaction failed on SIGABRT");
294  }
295  if (sigaction(SIGPIPE, &act, NULL) != 0) {
296  err(1, "sigaction failed on SIGPIPE");
297  }
298 }
299 
300 
301 //*****************************************************************
302 // system server
303 //*****************************************************************
304 
305 static void
306 do_query(DiscoverFnTy fn_discovery, struct syserv_mesg *mesg)
307 {
308  int ret;
309  long k;
310 
311  if (mesg->len > inbuf_size) {
312  inbuf_size += mesg->len;
313  inbuf = (char *) realloc(inbuf, inbuf_size);
314  if (inbuf == NULL) {
315  err(1, "realloc for inbuf failed");
316  }
317  }
318 
319  ret = read_all(fdin, inbuf, mesg->len);
320  if (ret != SUCCESS) {
321  err(1, "read from fdin failed");
322  }
323 
324  num_addrs = 0;
325  total_num_addrs = 0;
326  max_num_addrs = 0;
327  sent_ok_mesg = 0;
328 
329  if (sigsetjmp(jmpbuf, 1) == 0) {
330  // initial return on success
331  jmpbuf_ok = 1;
332  memset(&fnb_info, 0, sizeof(fnb_info));
335 
336  dump_file_info(inbuf, fn_discovery, SYSERV_QUERY);
337  jmpbuf_ok = 0;
338 
339  // pad list of addrs in case there are fewer function addrs than
340  // size of map.
342  for (k = total_num_addrs; k < max_num_addrs; k++) {
343  syserv_add_addr(NULL, 0);
344  }
345  if (num_addrs > 0) {
346  ret = write_all(fdout, addr_buf, num_addrs * sizeof(void *));
347  if (ret != SUCCESS) {
348  errx(1, "write to fdout failed");
349  }
350  num_addrs = 0;
351  }
352 
353  // add rusage maxrss to allow client to track memory usage.
354  // units are Kbytes
355  struct rusage usage;
356  if (getrusage(RUSAGE_SELF, &usage) == 0) {
357  fnb_info.memsize = usage.ru_maxrss;
358  } else {
359  fnb_info.memsize = -1;
360  }
361 
364  ret = write_all(fdout, &fnb_info, sizeof(fnb_info));
365  if (ret != SUCCESS) {
366  err(1, "write to fdout failed");
367  }
368  }
369  else if (sent_ok_mesg) {
370  // failed return from long jmp after we've told the client ok.
371  // for now, close the pipe and exit.
372  errx(1, "caught signal after telling client ok");
373  }
374  else {
375  // failed return from long jmp before we've told the client ok.
376  // in this case, we can send an ERR mesg.
377  ret = write_mesg(SYSERV_ERR, 0);
378  if (ret != SUCCESS) {
379  errx(1, "write to fdout failed");
380  }
381  }
382 }
383 
384 
385 /***
386  * query static variable:
387  * send a list of addresses of the static variable if exists
388  */
389 static void
390 do_query_var(DiscoverFnTy fn_discovery, struct syserv_mesg *mesg)
391 {
392  int ret;
393  long k;
394 
395  if (mesg->len > inbuf_size) {
396  inbuf_size += mesg->len;
397  inbuf = (char *) realloc(inbuf, inbuf_size);
398  if (inbuf == NULL) {
399  err(1, "realloc for inbuf failed");
400  }
401  }
402 
403  ret = read_all(fdin, inbuf, mesg->len);
404  if (ret != SUCCESS) {
405  err(1, "read from fdin failed");
406  }
407 
408  num_addrs = 0;
409  total_num_addrs = 0;
410  max_num_addrs = 0;
411  sent_ok_mesg = 0;
412 
413  if (sigsetjmp(jmpbuf, 1) == 0) {
414  // initial return on success
415  jmpbuf_ok = 1;
416  memset(&fnb_info, 0, sizeof(fnb_info));
419 
420  dump_file_info(inbuf, fn_discovery, SYSERV_QUERY_VAR);
421  jmpbuf_ok = 0;
422 
423  // pad list of addrs in case there are fewer function addrs than
424  // size of map.
426  for (k = total_num_addrs; k < max_num_addrs; k++) {
427  syserv_add_addr(NULL, 0);
428  }
429  if (num_addrs > 0) {
430  ret = write_all(fdout, addr_buf, num_addrs * sizeof(void *));
431  if (ret != SUCCESS) {
432  errx(1, "write to fdout failed");
433  }
434  num_addrs = 0;
435  }
436 
437  // add rusage maxrss to allow client to track memory usage.
438  // units are Kbytes
439  struct rusage usage;
440  if (getrusage(RUSAGE_SELF, &usage) == 0) {
441  fnb_info.memsize = usage.ru_maxrss;
442  } else {
443  fnb_info.memsize = -1;
444  }
445 
448  ret = write_all(fdout, &fnb_info, sizeof(fnb_info));
449  if (ret != SUCCESS) {
450  err(1, "write to fdout failed");
451  }
452  }
453  else if (sent_ok_mesg) {
454  // failed return from long jmp after we've told the client ok.
455  // for now, close the pipe and exit.
456  errx(1, "caught signal after telling client ok");
457  }
458  else {
459  // failed return from long jmp before we've told the client ok.
460  // in this case, we can send an ERR mesg.
461  ret = write_mesg(SYSERV_ERR, 0);
462  if (ret != SUCCESS) {
463  errx(1, "write to fdout failed");
464  }
465  }
466 }
467 
468 
469 void
470 system_server(DiscoverFnTy fn_discovery, int fd1, int fd2)
471 {
472  struct syserv_mesg mesg;
473 
474  fdin = fd1;
475  fdout = fd2;
476 
478  inbuf = (char *) malloc(inbuf_size);
479  if (inbuf == NULL) {
480  err(1, "malloc for inbuf failed");
481  }
483 
484  for (;;) {
485  int ret = read_mesg(&mesg);
486 
487  // failure on read from pipe
488  if (ret == FAILURE) {
489  err(1, "read from fdin failed");
490  }
491 
492  // exit
493  if (ret == END_OF_FILE || mesg.type == SYSERV_EXIT) {
494  break;
495  }
496 
497  // ack
498  if (mesg.type == SYSERV_ACK) {
500  }
501 
502  // query
503  else if (mesg.type == SYSERV_QUERY) {
505  do_query(fn_discovery, &mesg);
506  }
507  else if (mesg.type == SYSERV_QUERY_VAR) {
509  do_query_var(fn_discovery, &mesg);
510  }
511 
512  // unknown message
513  else {
514  err(1, "unknown mesg type from client: %d", mesg.type);
515  }
516  }
517 
518  exit(0);
519 }
static void signal_handler(int sig)
Definition: server.cpp:261
ssize_t MONITOR_EXT_WRAP_NAME() write(int fd, const void *buf, size_t count)
Definition: io-over.c:189
static int jmpbuf_ok
Definition: server.cpp:117
void system_server(DiscoverFnTy fn_discovery, int fd1, int fd2)
Definition: server.cpp:470
siglongjmp
Definition: names.cpp:1
static void * addr_buf[ADDR_SIZE]
Definition: server.cpp:107
err
Definition: names.cpp:1
static int fdout
Definition: server.cpp:105
static int read_mesg(struct syserv_mesg *mesg)
Definition: server.cpp:182
void code_ranges_reinit(void)
Definition: code-ranges.cpp:96
static void signal_handler_init(void)
Definition: server.cpp:278
static long max_num_addrs
Definition: server.cpp:110
static long total_num_addrs
Definition: server.cpp:109
Definition: fmt.c:108
#define FNBOUNDS_MAGIC
Definition: syserv-mesg.h:63
#define END_OF_FILE
Definition: server.cpp:102
static int read_all(int fd, void *buf, size_t count)
Definition: server.cpp:131
static void do_query(DiscoverFnTy fn_discovery, struct syserv_mesg *mesg)
Definition: server.cpp:306
#define ADDR_SIZE
Definition: server.cpp:97
#define SYSERV_MAGIC
Definition: syserv-mesg.h:62
static long num_addrs
Definition: server.cpp:108
static void do_query_var(DiscoverFnTy fn_discovery, struct syserv_mesg *mesg)
Definition: server.cpp:390
static int fdin
Definition: server.cpp:104
static struct syserv_fnbounds_info fnb_info
Definition: server.cpp:115
static int write_all(int fd, const void *buf, size_t count)
Definition: server.cpp:158
exit
Definition: names.cpp:1
static sigjmp_buf jmpbuf
Definition: server.cpp:118
int64_t len
Definition: syserv-mesg.h:77
void syserv_add_addr(void *addr, long func_entry_map_size)
Definition: server.cpp:218
DiscoverFnTy
Definition: code-ranges.h:52
#define FAILURE
Definition: server.cpp:101
static int sent_ok_mesg
Definition: server.cpp:120
int32_t type
Definition: syserv-mesg.h:76
void *MONITOR_EXT_WRAP_NAME() realloc(void *ptr, size_t bytes)
#define SUCCESS
Definition: server.cpp:100
void dump_file_info(const char *filename, DiscoverFnTy fn_discovery, int query)
Definition: main.cpp:482
void variable_entries_reinit(void)
int32_t magic
Definition: syserv-mesg.h:75
ssize_t MONITOR_EXT_WRAP_NAME() read(int fd, void *buf, size_t count)
Definition: io-over.c:152
void *MONITOR_EXT_WRAP_NAME() malloc(size_t bytes)
#define NULL
Definition: ElfHelper.cpp:85
#define INIT_INBUF_SIZE
Definition: server.cpp:98
void function_entries_reinit(void)
cct_addr_t * addr
Definition: cct.c:130
errx
Definition: names.cpp:1
static long inbuf_size
Definition: server.cpp:113
void syserv_add_header(int is_relocatable, uintptr_t ref_offset)
Definition: server.cpp:249
uint64_t reference_offset
Definition: syserv-mesg.h:88
static int write_mesg(int32_t type, int64_t len)
Definition: server.cpp:200
static char * inbuf
Definition: server.cpp:112