HPCToolkit
io-over.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 adds the IO sampling source: number of bytes read and
54 // written. This covers both stream IO (fread, fwrite, etc) and
55 // unbuffered IO (read, write, etc).
56 //
57 // Note: for the slow or blocking overrides, we record samples before
58 // and after the function. If a process blocks in kernel, then it
59 // won't receive async interrupts and this may under report the time
60 // in the trace. Using two samples assures that we see the full span
61 // of the function in the trace viewer.
62 //
63 // TODO list:
64 //
65 // 2. In the second sample (after the real function), want to record
66 // the sample without doing a full unwind (which is the same path as
67 // the first sample). This may require a little refactoring in the
68 // sample event code.
69 //
70 // 3. When taking the user context, replace the syscall with the
71 // assembler macros. This may require a little refactoring of the
72 // macros so that get context is just one line, not a #ifdef sequence.
73 // Also, need to fix or disable the i386 case.
74 //
75 // 4. Figure out the automake way to strip debug symbols from a static
76 // archive. Currently, this only works in the dynamic case.
77 //
78 // 5. Add overrides for printf, fprintf, fputs, etc.
79 //
80 //***************************************************************************
81 
82 /******************************************************************************
83  * standard include files
84  *****************************************************************************/
85 
86 #include <sys/types.h>
87 #include <errno.h>
88 #include <stdio.h>
89 #include <ucontext.h>
90 #include <unistd.h>
91 
92 
93 /******************************************************************************
94  * local include files
95  *****************************************************************************/
96 
97 #include <main.h>
98 #include <safe-sampling.h>
99 #include <sample_event.h>
100 #include <thread_data.h>
101 
102 #include <messages/messages.h>
104 #include <sample-sources/io.h>
105 
106 
107 /******************************************************************************
108  * type definitions
109  *****************************************************************************/
110 
111 typedef ssize_t read_fn_t(int, void *, size_t);
112 typedef ssize_t write_fn_t(int, const void *, size_t);
113 
114 typedef size_t fread_fn_t(void *, size_t, size_t, FILE *);
115 typedef size_t fwrite_fn_t(const void *, size_t, size_t, FILE *);
116 
117 
118 /******************************************************************************
119  * macros
120  *****************************************************************************/
121 
122 // Dynamically, there are two ways to get the real version of library
123 // functions: the __libc_/_IO_ names or dlsym(). The __libc_ names
124 // are GNU libc specific and may not be portable, and dlsym() may
125 // interfere with our code via locks or override functions. We'll try
126 // the _IO_ names until we hit a problem. Statically, we always use
127 // __wrap and __real.
128 
129 #ifdef HPCRUN_STATIC_LINK
130 #define real_read __real_read
131 #define real_write __real_write
132 #define real_fread __real_fread
133 #define real_fwrite __real_fwrite
134 #else
135 #define real_read __read
136 #define real_write __write
137 #define real_fread _IO_fread
138 #define real_fwrite _IO_fwrite
139 #endif
140 
141 extern read_fn_t real_read;
142 extern write_fn_t real_write;
143 extern fread_fn_t real_fread;
144 extern fwrite_fn_t real_fwrite;
145 
146 
147 /******************************************************************************
148  * interface operations
149  *****************************************************************************/
150 
151 ssize_t
152 MONITOR_EXT_WRAP_NAME(read)(int fd, void *buf, size_t count)
153 {
154  ucontext_t uc;
155  ssize_t ret;
157  int save_errno;
158 
159  if (metric_id_read < 0 || ! hpcrun_safe_enter()) {
160  return real_read(fd, buf, count);
161  }
162 
163  // insert samples before and after the slow functions to make the
164  // traces look better.
165  getcontext(&uc);
166  hpcrun_sample_callpath(&uc, metric_id_read,
167  (hpcrun_metricVal_t) {.i=0},
168  0, 1, NULL);
169 
171  ret = real_read(fd, buf, count);
172  save_errno = errno;
174 
175  // FIXME: the second sample should not do a full unwind.
176  TMSG(IO, "read: fd: %d, buf: %p, count: %ld, actual: %ld",
177  fd, buf, count, ret);
178  hpcrun_sample_callpath(&uc, metric_id_read,
179  (hpcrun_metricVal_t) {.i=(ret > 0 ? ret : 0)},
180  0, 1, NULL);
182 
183  errno = save_errno;
184  return ret;
185 }
186 
187 
188 ssize_t
189 MONITOR_EXT_WRAP_NAME(write)(int fd, const void *buf, size_t count)
190 {
191  ucontext_t uc;
192  size_t ret;
194  int save_errno;
195 
196  if (metric_id_write < 0 || ! hpcrun_safe_enter()) {
197  return real_write(fd, buf, count);
198  }
199 
200  // insert samples before and after the slow functions to make the
201  // traces look better.
202  getcontext(&uc);
203  hpcrun_sample_callpath(&uc, metric_id_write,
204  (hpcrun_metricVal_t) {.i=0},
205  0, 1, NULL);
206 
208  ret = real_write(fd, buf, count);
209  save_errno = errno;
211 
212  // FIXME: the second sample should not do a full unwind.
213  TMSG(IO, "write: fd: %d, buf: %p, count: %ld, actual: %ld",
214  fd, buf, count, ret);
215  hpcrun_sample_callpath(&uc, metric_id_write,
216  (hpcrun_metricVal_t) {.i=(ret > 0 ? ret : 0)},
217  0, 1, NULL);
219 
220  errno = save_errno;
221  return ret;
222 }
223 
224 
225 size_t
226 MONITOR_EXT_WRAP_NAME(fread)(void *ptr, size_t size, size_t count, FILE *stream)
227 {
228  ucontext_t uc;
229  size_t ret;
231 
232  if (metric_id_read < 0 || ! hpcrun_safe_enter()) {
233  return real_fread(ptr, size, count, stream);
234  }
235 
236  // insert samples before and after the slow functions to make the
237  // traces look better.
238  getcontext(&uc);
239  hpcrun_sample_callpath(&uc, metric_id_read,
240  (hpcrun_metricVal_t) {.i=0},
241  0, 1, NULL);
242 
244  ret = real_fread(ptr, size, count, stream);
246 
247  // FIXME: the second sample should not do a full unwind.
248  TMSG(IO, "fread: size: %ld, count: %ld, bytes: %ld, actual: %ld",
249  size, count, count*size, ret*size);
250  hpcrun_sample_callpath(&uc, metric_id_read,
251  (hpcrun_metricVal_t) {.i=ret*size},
252  0, 1, NULL);
254 
255  return ret;
256 }
257 
258 
259 size_t
260 MONITOR_EXT_WRAP_NAME(fwrite)(const void *ptr, size_t size, size_t count,
261  FILE *stream)
262 {
263  ucontext_t uc;
264  size_t ret;
266 
267  if (metric_id_write < 0 || ! hpcrun_safe_enter()) {
268  return real_fwrite(ptr, size, count, stream);
269  }
270 
271  // insert samples before and after the slow functions to make the
272  // traces look better.
273  getcontext(&uc);
274  hpcrun_sample_callpath(&uc, metric_id_write,
275  (hpcrun_metricVal_t) {.i=0},
276  0, 1, NULL);
277 
279  ret = real_fwrite(ptr, size, count, stream);
281 
282  // FIXME: the second sample should not do a full unwind.
283  TMSG(IO, "fwrite: size: %ld, count: %ld, bytes: %ld, actual: %ld",
284  size, count, count*size, ret*size);
285  hpcrun_sample_callpath(&uc, metric_id_write,
286  (hpcrun_metricVal_t) {.i=ret*size},
287  0, 1, NULL);
289 
290  return ret;
291 }
ssize_t write_fn_t(int, const void *, size_t)
Definition: io-over.c:112
ssize_t MONITOR_EXT_WRAP_NAME() write(int fd, const void *buf, size_t count)
Definition: io-over.c:189
int hpcrun_metric_id_write(void)
Definition: io.c:208
static void hpcrun_safe_exit(void)
sample_val_t hpcrun_sample_callpath(void *context, int metricId, hpcrun_metricVal_t metricIncr, int skipInner, int isSync, sampling_info_t *data)
Definition: sample_event.c:160
#define MONITOR_EXT_WRAP_NAME(name)
Definition: monitor_ext.h:92
Definition: fmt.c:108
#define real_fread
Definition: io-over.c:137
static int metric_id_write
Definition: io.c:76
size_t MONITOR_EXT_WRAP_NAME() fread(void *ptr, size_t size, size_t count, FILE *stream)
Definition: io-over.c:226
int hpcrun_metric_id_read(void)
Definition: io.c:202
static char buf[32]
Definition: StrUtil.cpp:240
static int metric_id_read
Definition: io.c:75
ssize_t read_fn_t(int, void *, size_t)
Definition: io-over.c:111
#define TMSG(f,...)
Definition: messages.h:93
ssize_t MONITOR_EXT_WRAP_NAME() read(int fd, void *buf, size_t count)
Definition: io-over.c:152
#define NULL
Definition: ElfHelper.cpp:85
size_t MONITOR_EXT_WRAP_NAME() fwrite(const void *ptr, size_t size, size_t count, FILE *stream)
Definition: io-over.c:260
unsigned char uc
Definition: amd-xop.c:3
#define real_write
Definition: io-over.c:136
#define real_fwrite
Definition: io-over.c:138
#define real_read
Definition: io-over.c:135
size_t fread_fn_t(void *, size_t, size_t, FILE *)
Definition: io-over.c:114
size_t fwrite_fn_t(const void *, size_t, size_t, FILE *)
Definition: io-over.c:115
static int hpcrun_safe_enter(void)