HPCToolkit
hpcio-buffer.c
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 //***************************************************************************
48 //
49 // File:
50 // $HeadURL$
51 //
52 // Purpose:
53 // This file implements a simple output buffer for hpcrun. The main
54 // reason for writing our own buffer is to have something that is safe
55 // inside signal handlers and doesn't interact with the standard I/O
56 // streams library, especially fflush().
57 //
58 // We don't need to reimplement all of stdio, just enough to meet the
59 // special needs of writing hpcrun and hpctrace files from hpcrun.
60 // Programs like hpcstruct and hpcprof should continue to use the
61 // stdio library.
62 //
63 // Note: this file should be careful only to use functions that are
64 // safe inside signal handlers. In particular, don't use malloc or
65 // functions that call malloc, and don't use the stdio library.
66 //
67 // Note: it's important for the client to call close (or at least
68 // flush) at the end of the process. We don't automatically gain
69 // control at the end of the process, so there is no way to auto close
70 // the buffers.
71 //
72 // Deserves further study: the best way to handle errors from write().
73 //
74 //***************************************************************************
75 
76 //************************* System Include Files ****************************
77 
78 #include <sys/types.h>
79 #include <sys/stat.h>
80 #include <errno.h>
81 #include <fcntl.h>
82 #include <string.h>
83 #include <unistd.h>
84 
85 
86 //*************************** User Include Files ****************************
87 
88 #include "hpcfmt.h"
89 #include "hpcio-buffer.h"
90 #include "spinlock.h"
91 #include <include/min-max.h>
92 
93 #define HPCIO_OUTBUF_MAGIC 0x494F4246
94 
95 
96 //*************************** Private Functions *****************************
97 
98 // Try to write() the entire outbuf.
99 //
100 // Returns: HPCFMT_OK if the entire buffer was successfully written,
101 // else HPCFMT_ERR.
102 //
103 static int
105 {
106  ssize_t amt_done, ret;
107 
108  amt_done = 0;
109  while (amt_done < outbuf->in_use) {
110  errno = 0;
111  ret = write(outbuf->fd, outbuf->buf_start + amt_done,
112  outbuf->in_use - amt_done);
113 
114  // Check for short writes. Note: EINTR is not failure.
115  if (ret > 0 || (ret == 0 && errno == EINTR)) {
116  // progress, continue
117  amt_done += ret;
118  }
119  else {
120  // hard failure
121  // FIXME: should do better than copy to begin of buffer.
122  // However, this case is rare, it probably will continue to fail
123  // and I want to rethink this case anyway. So, this will do for
124  // now. (krentel)
125  if (amt_done > 0) {
126  memmove(outbuf->buf_start, outbuf->buf_start + amt_done,
127  outbuf->in_use - amt_done);
128  outbuf->in_use = amt_done;
129  }
130  return HPCFMT_ERR;
131  }
132  }
133 
134  // entire buffer was successfully written
135  outbuf->in_use = 0;
136  return HPCFMT_OK;
137 }
138 
139 
140 //*************************** Interface Functions ***************************
141 
142 // Attach the file descriptor to the buffer, initialize and fill in
143 // the outbuf struct. The client supplies the buffer and fd.
144 //
145 // Returns: HPCFMT_OK on success, else HPCFMT_ERR.
146 //
147 int
148 hpcio_outbuf_attach(hpcio_outbuf_t *outbuf /* out */, int fd,
149  void *buf_start, size_t buf_size, int flags)
150 {
151  // Attach fills in the outbuf struct, so there is no magic number
152  // and locks don't exist.
153  if (outbuf == NULL || fd < 0 || buf_start == NULL || buf_size == 0) {
154  return HPCFMT_ERR;
155  }
156 
157  outbuf->magic = HPCIO_OUTBUF_MAGIC;
158  outbuf->buf_start = buf_start;
159  outbuf->buf_size = buf_size;
160  outbuf->in_use = 0;
161  outbuf->fd = fd;
162  outbuf->flags = flags;
163  outbuf->use_lock = (flags & HPCIO_OUTBUF_LOCKED);
164  spinlock_unlock(&outbuf->lock);
165 
166  return HPCFMT_OK;
167 }
168 
169 
170 // Copy data to the outbuf and flush if necessary.
171 //
172 // Returns: number of bytes copied, or else -1 on bad buffer.
173 //
174 ssize_t
175 hpcio_outbuf_write(hpcio_outbuf_t *outbuf, const void *data, size_t size)
176 {
177  size_t amt, amt_done;
178 
179  if (outbuf == NULL || outbuf->magic != HPCIO_OUTBUF_MAGIC) {
180  return -1;
181  }
182  if (outbuf->use_lock) {
183  spinlock_lock(&outbuf->lock);
184  }
185 
186  amt_done = 0;
187  while (amt_done < size) {
188  // flush if needed
189  if (size > outbuf->buf_size - outbuf->in_use) {
190  outbuf_flush_buffer(outbuf);
191  if (outbuf->in_use == outbuf->buf_size) {
192  // flush failed, no space
193  break;
194  }
195  }
196 
197  // copy as much as possible
198  amt = MIN(size - amt_done, outbuf->buf_size - outbuf->in_use);
199  memcpy(outbuf->buf_start + outbuf->in_use, data + amt_done, amt);
200  outbuf->in_use += amt;
201  amt_done += amt;
202  }
203 
204  if (outbuf->use_lock) {
205  spinlock_unlock(&outbuf->lock);
206  }
207  return amt_done;
208 }
209 
210 
211 // Flush the outbuf to the kernel via write().
212 //
213 // Returns: HPCFMT_OK on success, else HPCFMT_ERR.
214 //
215 int
217 {
218  if (outbuf == NULL || outbuf->magic != HPCIO_OUTBUF_MAGIC) {
219  return HPCFMT_ERR;
220  }
221  if (outbuf->use_lock) {
222  spinlock_lock(&outbuf->lock);
223  }
224 
225  int ret = outbuf_flush_buffer(outbuf);
226 
227  if (outbuf->use_lock) {
228  spinlock_unlock(&outbuf->lock);
229  }
230  return ret;
231 }
232 
233 
234 // Flush the outbuf and close() the file descriptor. Note: the client
235 // must explicitly call close at the end of the process. There is no
236 // auto close.
237 //
238 // Returns: HPCFMT_OK on success, else HPCFMT_ERR.
239 //
240 int
242 {
243  int ret = HPCFMT_OK;
244 
245  if (outbuf == NULL || outbuf->magic != HPCIO_OUTBUF_MAGIC) {
246  return HPCFMT_ERR;
247  }
248  if (outbuf->use_lock) {
249  spinlock_lock(&outbuf->lock);
250  }
251 
252  if (outbuf_flush_buffer(outbuf) == HPCFMT_OK
253  && close(outbuf->fd) == 0) {
254  // flush and close both succeed
255  outbuf->magic = 0;
256  outbuf->fd = -1;
257  }
258  else {
259  ret = HPCFMT_ERR;
260  }
261 
262  if (outbuf->use_lock) {
263  spinlock_unlock(&outbuf->lock);
264  }
265  return ret;
266 }
ssize_t MONITOR_EXT_WRAP_NAME() write(int fd, const void *buf, size_t count)
Definition: io-over.c:189
spinlock_t lock
Definition: hpcio-buffer.h:66
ssize_t hpcio_outbuf_write(hpcio_outbuf_t *outbuf, const void *data, size_t size)
Definition: hpcio-buffer.c:175
static void spinlock_unlock(spinlock_t *l)
Definition: spinlock.h:96
#define MIN(a, b)
Definition: min-max.h:76
#define HPCIO_OUTBUF_MAGIC
Definition: hpcio-buffer.c:93
uint32_t magic
Definition: hpcio-buffer.h:59
int hpcio_outbuf_close(hpcio_outbuf_t *outbuf)
Definition: hpcio-buffer.c:241
static void spinlock_lock(spinlock_t *l)
Definition: spinlock.h:111
static int outbuf_flush_buffer(hpcio_outbuf_t *outbuf)
Definition: hpcio-buffer.c:104
int hpcio_outbuf_attach(hpcio_outbuf_t *outbuf, int fd, void *buf_start, size_t buf_size, int flags)
Definition: hpcio-buffer.c:148
#define NULL
Definition: ElfHelper.cpp:85
#define HPCIO_OUTBUF_LOCKED
Definition: hpcio-buffer.h:74
void * buf_start
Definition: hpcio-buffer.h:60
int hpcio_outbuf_flush(hpcio_outbuf_t *outbuf)
Definition: hpcio-buffer.c:216