1 /* vim: ts=8 sw=4 expandtab:
2  * ************************************************************************
3  * This file is part of the Devel::NYTProf package.
4  * See http://metacpan.org/release/Devel-NYTProf/
5  * For Copyright see lib/Devel/NYTProf.pm
6  * For contribution history see repository logs.
7  * ************************************************************************
8  */
9 
10 #define PERL_NO_GET_CONTEXT                       /* we want efficiency */
11 #include "EXTERN.h"
12 #include "perl.h"
13 #include "XSUB.h"
14 #if defined(PERL_IMPLICIT_SYS) && !defined(NO_XSLOCKS)
15 #  ifndef fgets
16 #    define fgets PerlSIO_fgets
17 #  endif
18 #endif
19 
20 #include "FileHandle.h"
21 #include "NYTProf.h"
22 
23 #define NEED_sv_2pvbyte
24 #include "ppport.h"
25 
26 #ifdef HAS_ZLIB
27 #  include <zlib.h>
28 #endif
29 
30 #define NYTP_FILE_STDIO         0
31 #define NYTP_FILE_DEFLATE       1
32 #define NYTP_FILE_INFLATE       2
33 
34 /* to help find places in NYTProf.xs where we don't save/restore errno */
35 #if 0
36 #define ERRNO_PROBE errno=__LINE__
37 #else
38 #define ERRNO_PROBE (void)0
39 #endif
40 
41 /* During profiling the large buffer collects the raw data until full.
42  * Then flush_output zips it into the small buffer and writes it to disk.
43  * A scale factor of ~90 makes the large buffer usually almost fill the small
44  * one when zipped (so calls to flush_output() almost always trigger one fwrite()).
45  * We use a lower number to save some memory as there's little performance
46  * impact either way.
47  */
48 #define NYTP_FILE_SMALL_BUFFER_SIZE   4096
49 #define NYTP_FILE_LARGE_BUFFER_SIZE   (NYTP_FILE_SMALL_BUFFER_SIZE * 40)
50 
51 #ifdef HAS_ZLIB
52 #  define FILE_STATE(f)         ((f)->state)
53 #else
54 #  define FILE_STATE(f)         NYTP_FILE_STDIO
55 #endif
56 
57 #if defined(PERL_IMPLICIT_CONTEXT) && ! defined(tTHX)
58 #  define tTHX PerlInterpreter*
59 #endif
60 
61 struct NYTP_file_t {
62     FILE *file;
63 #ifdef PERL_IMPLICIT_CONTEXT
64     tTHX aTHX; /* on 5.8 and older, pTHX contains a "register" which is not
65                   compatible with a struct def, so use something else */
66 #endif
67 #ifdef HAS_ZLIB
68     unsigned char state;
69     bool stdio_at_eof;
70     bool zlib_at_eof;
71     /* For input only, the position we are in large_buffer.  */
72     unsigned int count;
73     z_stream zs;
74     unsigned char small_buffer[NYTP_FILE_SMALL_BUFFER_SIZE];
75     unsigned char large_buffer[NYTP_FILE_LARGE_BUFFER_SIZE];
76 #endif
77 };
78 
79 /* unlike dTHX which contains a function call, and therefore can never be
80   optimized away, even if return value isn't used, the below will optimize away
81   if NO_XSLOCKS is defined and PerlIO is not being used (i.e. native C lib
82   IO is being used on Win32 )*/
83 #ifdef PERL_IMPLICIT_CONTEXT
84 #  define dNFTHX(x) dTHXa((x)->aTHX)
85 #else
86 #  define dNFTHX(x) dNOOP
87 #endif
88 
89 /* XXX The proper return value would be Off_t */
90 long
NYTP_tell(NYTP_file file)91 NYTP_tell(NYTP_file file) {
92     ERRNO_PROBE;
93 
94 #ifdef HAS_ZLIB
95     /* This has to work with compressed files as it's used in the croaking
96        routine.  */
97     if (FILE_STATE(file) != NYTP_FILE_STDIO) {
98         return FILE_STATE(file) == NYTP_FILE_INFLATE
99             ? file->zs.total_out : file->zs.total_in;
100     }
101 #endif
102     {
103         dNFTHX(file);
104         return (long)ftell(file->file);
105     }
106 }
107 
108 #ifdef HAS_ZLIB
109 const char *
NYTP_type_of_offset(NYTP_file file)110 NYTP_type_of_offset(NYTP_file file) {
111     switch (FILE_STATE(file)) {
112     case NYTP_FILE_STDIO:
113         return "";
114     case NYTP_FILE_DEFLATE:
115         return " in compressed output data";
116         break;
117     case NYTP_FILE_INFLATE:
118         return " in compressed input data";
119         break;
120     default:
121         return Perl_form_nocontext(" in stream in unknown state %d",
122                                    FILE_STATE(file));
123     }
124 }
125 #endif
126 
127 #ifdef HAS_ZLIB
128 #  define CROAK_IF_NOT_STDIO(file, where)           \
129     STMT_START {                                    \
130         if (FILE_STATE(file) != NYTP_FILE_STDIO) {  \
131             compressed_io_croak((file), (where));   \
132         }                                           \
133     } STMT_END
134 #else
135 #  define CROAK_IF_NOT_STDIO(file, where)
136 #endif
137 
138 #ifdef HAS_ZLIB
139 #ifdef HASATTRIBUTE_NORETURN
140 __attribute__noreturn__
141 #endif
142 static void
compressed_io_croak(NYTP_file file,const char * function)143 compressed_io_croak(NYTP_file file, const char *function) {
144     const char *what;
145 
146     switch (FILE_STATE(file)) {
147     case NYTP_FILE_STDIO:
148         what = "stdio";
149         break;
150     case NYTP_FILE_DEFLATE:
151         what = "compressed output";
152         break;
153     case NYTP_FILE_INFLATE:
154         what = "compressed input";
155         break;
156     default:
157         croak("Can't use function %s() on a stream of type %d at offset %ld",
158               function, FILE_STATE(file), NYTP_tell(file));
159     }
160     croak("Can't use function %s() on a %s stream at offset %ld", function,
161           what, NYTP_tell(file));
162 }
163 
164 void
NYTP_start_deflate(NYTP_file file,int compression_level)165 NYTP_start_deflate(NYTP_file file, int compression_level) {
166     int status;
167     ERRNO_PROBE;
168 
169     CROAK_IF_NOT_STDIO(file, "NYTP_start_deflate");
170     FILE_STATE(file) = NYTP_FILE_DEFLATE;
171     file->zs.next_in = (Bytef *) file->large_buffer;
172     file->zs.avail_in = 0;
173     file->zs.next_out = (Bytef *) file->small_buffer;
174     file->zs.avail_out = NYTP_FILE_SMALL_BUFFER_SIZE;
175     file->zs.zalloc = (alloc_func) 0;
176     file->zs.zfree = (free_func) 0;
177     file->zs.opaque = 0;
178 
179     status = deflateInit2(&(file->zs), compression_level, Z_DEFLATED,
180         15 /* windowBits */,
181         9 /* memLevel */, Z_DEFAULT_STRATEGY);
182     if (status != Z_OK) {
183         croak("deflateInit2 failed, error %d (%s)", status, file->zs.msg);
184     }
185 }
186 
187 void
NYTP_start_inflate(NYTP_file file)188 NYTP_start_inflate(NYTP_file file) {
189     int status;
190     ERRNO_PROBE;
191 
192     CROAK_IF_NOT_STDIO(file, "NYTP_start_inflate");
193     FILE_STATE(file) = NYTP_FILE_INFLATE;
194 
195     file->zs.next_in = (Bytef *) file->small_buffer;
196     file->zs.avail_in = 0;
197     file->zs.next_out = (Bytef *) file->large_buffer;
198     file->zs.avail_out = NYTP_FILE_LARGE_BUFFER_SIZE;
199     file->zs.zalloc = (alloc_func) 0;
200     file->zs.zfree = (free_func) 0;
201     file->zs.opaque = 0;
202 
203     status = inflateInit2(&(file->zs), 15);
204     if (status != Z_OK) {
205         croak("inflateInit2 failed, error %d (%s)", status, file->zs.msg);
206     }
207 }
208 #endif
209 
210 NYTP_file
NYTP_open(const char * name,const char * mode)211 NYTP_open(const char *name, const char *mode) {
212     dTHX;
213     FILE *raw_file = fopen(name, mode);
214     NYTP_file file;
215     ERRNO_PROBE;
216 
217     if (!raw_file)
218         return NULL;
219 
220 /* MS libc has 4096 as default, this is too slow for GB size profiling data */
221     if (setvbuf(raw_file, NULL, _IOFBF, 16384))
222         return NULL;
223     Newx(file, 1, struct NYTP_file_t);
224     file->file = raw_file;
225 
226 #ifdef PERL_IMPLICIT_CONTEXT
227     file->aTHX = aTHX;
228 #endif
229 #ifdef HAS_ZLIB
230     file->state = NYTP_FILE_STDIO;
231     file->count = 0;
232     file->stdio_at_eof = FALSE;
233     file->zlib_at_eof = FALSE;
234 
235     file->zs.msg = (char *)"[Oops. zlib hasn't updated this error string]";
236 #endif
237 
238     return file;
239 }
240 
241 #ifdef HAS_ZLIB
242 
243 static void
grab_input(NYTP_file ifile)244 grab_input(NYTP_file ifile) {
245     dNFTHX(ifile);
246     ERRNO_PROBE;
247 
248     ifile->count = 0;
249     ifile->zs.next_out = (Bytef *) ifile->large_buffer;
250     ifile->zs.avail_out = NYTP_FILE_LARGE_BUFFER_SIZE;
251 
252 #ifdef DEBUG_INFLATE
253     fprintf(stderr, "grab_input enter\n");
254 #endif
255 
256     while (1) {
257         int status;
258 
259         if (ifile->zs.avail_in == 0 && !ifile->stdio_at_eof) {
260             size_t got = fread(ifile->small_buffer, 1,
261                                NYTP_FILE_SMALL_BUFFER_SIZE, ifile->file);
262 
263             if (got == 0) {
264                 if (!feof(ifile->file)) {
265                     int eno = errno;
266                     croak("grab_input failed: %d (%s)", eno, strerror(eno));
267                 }
268                 ifile->stdio_at_eof = TRUE;
269             }
270 
271             ifile->zs.avail_in = got;
272             ifile->zs.next_in = (Bytef *) ifile->small_buffer;
273         }
274 
275 #ifdef DEBUG_INFLATE
276         fprintf(stderr, "grab_input predef  next_in= %p avail_in= %08x\n"
277                         "                   next_out=%p avail_out=%08x"
278                 " eof=%d,%d\n", ifile->zs.next_in, ifile->zs.avail_in,
279                 ifile->zs.next_out, ifile->zs.avail_out, ifile->stdio_at_eof,
280                 ifile->zlib_at_eof);
281 #endif
282 
283         status = inflate(&(ifile->zs), Z_NO_FLUSH);
284 
285 #ifdef DEBUG_INFLATE
286         fprintf(stderr, "grab_input postdef next_in= %p avail_in= %08x\n"
287                         "                   next_out=%p avail_out=%08x "
288                 "status=%d\n", ifile->zs.next_in, ifile->zs.avail_in,
289                 ifile->zs.next_out, ifile->zs.avail_out, status);
290 #endif
291 
292         if (!(status == Z_OK || status == Z_STREAM_END)) {
293             if (ifile->stdio_at_eof)
294                 croak("Profile data incomplete, inflate error %d (%s) at end of input file,"
295                     " perhaps the process didn't exit cleanly or the file has been truncated "
296                     " (refer to TROUBLESHOOTING in the documentation)\n",
297                     status, ifile->zs.msg);
298             croak("Error reading file: inflate failed, error %d (%s) at offset %ld in input file",
299                   status, ifile->zs.msg, (long)ftell(ifile->file));
300         }
301 
302         if (ifile->zs.avail_out == 0 || status == Z_STREAM_END) {
303             if (status == Z_STREAM_END) {
304                 ifile->zlib_at_eof = TRUE;
305             }
306             return;
307         }
308     }
309 }
310 
311 #endif
312 
313 
314 size_t
NYTP_read_unchecked(NYTP_file ifile,void * buffer,size_t len)315 NYTP_read_unchecked(NYTP_file ifile, void *buffer, size_t len) {
316     dNFTHX(ifile);
317 #ifdef HAS_ZLIB
318     size_t result = 0;
319 #endif
320     ERRNO_PROBE;
321     if (FILE_STATE(ifile) == NYTP_FILE_STDIO) {
322         return fread(buffer, 1, len, ifile->file);
323     }
324 #ifdef HAS_ZLIB
325     else if (FILE_STATE(ifile) != NYTP_FILE_INFLATE) {
326         compressed_io_croak(ifile, "NYTP_read");
327         return 0;
328     }
329     while (1) {
330         unsigned char *p = ifile->large_buffer + ifile->count;
331         int remaining = ((unsigned char *) ifile->zs.next_out) - p;
332 
333         if (remaining >= len) {
334             Copy(p, buffer, len, unsigned char);
335             ifile->count += len;
336             result += len;
337             return result;
338         }
339         Copy(p, buffer, remaining, unsigned char);
340         ifile->count = NYTP_FILE_LARGE_BUFFER_SIZE;
341         result += remaining;
342         len -= remaining;
343         buffer = (void *)(remaining + (char *)buffer);
344         if (ifile->zlib_at_eof)
345             return result;
346         grab_input(ifile);
347     }
348 #endif
349 }
350 
351 
352 size_t
NYTP_read(NYTP_file ifile,void * buffer,size_t len,const char * what)353 NYTP_read(NYTP_file ifile, void *buffer, size_t len, const char *what) {
354     size_t got = NYTP_read_unchecked(ifile, buffer, len);
355     if (got != len) {
356         croak("Profile format error whilst reading %s at %ld%s: expected %ld got %ld, %s (see TROUBLESHOOTING in docs)",
357               what, NYTP_tell(ifile), NYTP_type_of_offset(ifile), (long)len, (long)got,
358                 (NYTP_eof(ifile)) ? "end of file" : NYTP_fstrerror(ifile));
359     }
360     return len;
361 }
362 
363 /* This isn't exactly fgets. It will resize the buffer as needed, and returns
364    a pointer to one beyond the read data (usually the terminating '\0'), or
365    NULL if it hit error/EOF */
366 
367 char *
NYTP_gets(NYTP_file ifile,char ** buffer_p,size_t * len_p)368 NYTP_gets(NYTP_file ifile, char **buffer_p, size_t *len_p) {
369     char *buffer = *buffer_p;
370     size_t len = *len_p;
371     size_t prev_len = 0;
372     ERRNO_PROBE;
373 
374 #ifdef HAS_ZLIB
375     if (FILE_STATE(ifile) == NYTP_FILE_INFLATE) {
376         while (1) {
377             const unsigned char *const p = ifile->large_buffer + ifile->count;
378             const unsigned int remaining = ((unsigned char *) ifile->zs.next_out) - p;
379             unsigned char *const nl = (unsigned char *)memchr(p, '\n', remaining);
380             size_t got;
381             size_t want;
382             size_t extra;
383 
384             if (nl) {
385                 want = nl + 1 - p;
386                 extra = want + 1; /* 1 more to add a \0 */
387             } else {
388                 want = extra = remaining;
389             }
390 
391             if (extra > len - prev_len) {
392                 prev_len = len;
393                 len += extra;
394                 buffer = (char *)saferealloc(buffer, len);
395             }
396 
397             got = NYTP_read_unchecked(ifile, buffer + prev_len, want);
398             if (got != want)
399                 croak("NYTP_gets unexpected short read. got %lu, expected %lu\n",
400                       (unsigned long)got, (unsigned long)want);
401 
402             if (nl) {
403                 buffer[prev_len + want] = '\0';
404                 *buffer_p = buffer;
405                 *len_p = len;
406                 return buffer + prev_len + want;
407             }
408             if (ifile->zlib_at_eof) {
409                 *buffer_p = buffer;
410                 *len_p = len;
411                 return NULL;
412             }
413             grab_input(ifile);
414         }
415     }
416 #endif
417     CROAK_IF_NOT_STDIO(ifile, "NYTP_gets");
418 
419     {
420         dNFTHX(ifile);
421         while(fgets(buffer + prev_len, (int)(len - prev_len), ifile->file)) {
422             /* We know that there are no '\0' bytes in the part we've already
423                read, so don't bother running strlen() over that part.  */
424             char *end = buffer + prev_len + strlen(buffer + prev_len);
425             if (end[-1] == '\n') {
426                 *buffer_p = buffer;
427                 *len_p = len;
428                 return end;
429             }
430             prev_len = len - 1; /* -1 to take off the '\0' at the end */
431             len *= 2;
432             buffer = (char *)saferealloc(buffer, len);
433         }
434     }
435     *buffer_p = buffer;
436     *len_p = len;
437     return NULL;
438 }
439 
440 
441 #ifdef HAS_ZLIB
442 /* Cheat, by telling zlib about a reduced amount of available output space,
443    such that our next write of the (slightly underused) output buffer will
444    align the underlying file pointer back with the size of our output buffer
445    (and hopefully the underlying OS block writes).  */
446 static void
sync_avail_out_to_ftell(NYTP_file ofile)447 sync_avail_out_to_ftell(NYTP_file ofile) {
448     dNFTHX(ofile);
449     const long result = ftell(ofile->file);
450     const unsigned long where = result < 0 ? 0 : result;
451     ERRNO_PROBE;
452     ofile->zs.avail_out =
453         NYTP_FILE_SMALL_BUFFER_SIZE - where % NYTP_FILE_SMALL_BUFFER_SIZE;
454 #ifdef DEBUG_DEFLATE
455     fprintf(stderr, "sync_avail_out_to_ftell pos=%ld, avail_out=%lu\n",
456             result, (unsigned long) ofile->zs.avail_out);
457 #endif
458 }
459 
460 /* flush has values as described for "allowed flush values" in zlib.h  */
461 static void
flush_output(NYTP_file ofile,int flush)462 flush_output(NYTP_file ofile, int flush) {
463     dNFTHX(ofile);
464     ERRNO_PROBE;
465 
466     ofile->zs.next_in = (Bytef *) ofile->large_buffer;
467 
468 #ifdef DEBUG_DEFLATE
469     fprintf(stderr, "flush_output enter   flush = %d\n", flush);
470 #endif
471     while (1) {
472         int status;
473 #ifdef DEBUG_DEFLATE
474         fprintf(stderr, "flush_output predef  next_in= %p avail_in= %08x\n"
475                         "                     next_out=%p avail_out=%08x"
476                 " flush=%d\n", ofile->zs.next_in, ofile->zs.avail_in,
477                 ofile->zs.next_out, ofile->zs.avail_out, flush);
478 #endif
479         status = deflate(&(ofile->zs), flush);
480 
481         /* workaround for RT#50851 */
482         if (status == Z_BUF_ERROR && flush != Z_NO_FLUSH
483                 && !ofile->zs.avail_in && ofile->zs.avail_out)
484             status = Z_OK;
485 
486 #ifdef DEBUG_DEFLATE
487         fprintf(stderr, "flush_output postdef next_in= %p avail_in= %08x\n"
488                         "                     next_out=%p avail_out=%08x "
489                 "status=%d\n", ofile->zs.next_in, ofile->zs.avail_in,
490                 ofile->zs.next_out, ofile->zs.avail_out, status);
491 #endif
492 
493         if (status == Z_OK || status == Z_STREAM_END) {
494             if (ofile->zs.avail_out == 0 || flush != Z_NO_FLUSH) {
495                 int terminate
496                     = ofile->zs.avail_in == 0 && ofile->zs.avail_out > 0;
497                 size_t avail = ((unsigned char *) ofile->zs.next_out)
498                     - ofile->small_buffer;
499                 const unsigned char *where = ofile->small_buffer;
500 
501                 while (avail > 0) {
502                     size_t count = fwrite(where, 1, avail, ofile->file);
503 
504                     if (count > 0) {
505                         where += count;
506                         avail -= count;
507                     } else {
508                         int eno = errno;
509                         croak("fwrite in flush error %d: %s", eno,
510                               strerror(eno));
511                     }
512                 }
513                 ofile->zs.next_out = (Bytef *) ofile->small_buffer;
514                 ofile->zs.avail_out = NYTP_FILE_SMALL_BUFFER_SIZE;
515                 if (terminate) {
516                     ofile->zs.avail_in = 0;
517                     if (flush == Z_SYNC_FLUSH) {
518                         sync_avail_out_to_ftell(ofile);
519                     }
520                     return;
521                 }
522             } else {
523                 ofile->zs.avail_in = 0;
524                 return;
525             }
526         } else {
527             croak("deflate(%ld,%d) failed, error %d (%s) in pid %d",
528                 (long)ofile->zs.avail_in, flush, status, ofile->zs.msg, getpid());
529         }
530     }
531 }
532 #endif
533 
534 size_t
NYTP_write(NYTP_file ofile,const void * buffer,size_t len)535 NYTP_write(NYTP_file ofile, const void *buffer, size_t len) {
536 #ifdef HAS_ZLIB
537     size_t result = 0;
538 #endif
539     ERRNO_PROBE;
540 
541     if (FILE_STATE(ofile) == NYTP_FILE_STDIO) {
542         /* fwrite with len==0 is problematic */
543         /* http://www.opengroup.org/platform/resolutions/bwg98-007.html */
544         if (len == 0)
545             return len;
546         {
547             dNFTHX(ofile);
548             if (fwrite(buffer, 1, len, ofile->file) < 1) {
549                 int eno = errno;
550                 croak("fwrite error %d writing %ld bytes to fd%d: %s",
551                     eno, (long)len, fileno(ofile->file), strerror(eno));
552             }
553         }
554         return len;
555     }
556 #ifdef HAS_ZLIB
557     else if (FILE_STATE(ofile) != NYTP_FILE_DEFLATE) {
558         compressed_io_croak(ofile, "NYTP_write");
559         return 0;
560     }
561     while (1) {
562         int remaining = NYTP_FILE_LARGE_BUFFER_SIZE - ofile->zs.avail_in;
563         unsigned char *p = ofile->large_buffer + ofile->zs.avail_in;
564 
565         if (remaining >= len) {
566             Copy(buffer, p, len, unsigned char);
567             ofile->zs.avail_in += len;
568             result += len;
569             return result;
570         } else {
571             /* Copy what we can, then flush the buffer. Lather, rinse, repeat.
572              */
573             Copy(buffer, p, remaining, unsigned char);
574             ofile->zs.avail_in = NYTP_FILE_LARGE_BUFFER_SIZE;
575             result += remaining;
576             len -= remaining;
577             buffer = (void *)(remaining + (char *)buffer);
578             flush_output(ofile, Z_NO_FLUSH);
579         }
580     }
581 #endif
582 }
583 
584 int
NYTP_printf(NYTP_file ofile,const char * format,...)585 NYTP_printf(NYTP_file ofile, const char *format, ...) {
586     int retval;
587     va_list args;
588     ERRNO_PROBE;
589 
590     CROAK_IF_NOT_STDIO(ofile, "NYTP_printf");
591 
592     va_start(args, format);
593     {
594         dNFTHX(ofile);
595         retval = vfprintf(ofile->file, format, args);
596     }
597     va_end(args);
598 
599     return retval;
600 }
601 
602 int
NYTP_flush(NYTP_file file)603 NYTP_flush(NYTP_file file) {
604     ERRNO_PROBE;
605 #ifdef HAS_ZLIB
606     if (FILE_STATE(file) == NYTP_FILE_DEFLATE) {
607         flush_output(file, Z_SYNC_FLUSH);
608     }
609 #endif
610     {
611         dNFTHX(file);
612         return fflush(file->file);
613     }
614 }
615 
616 int
NYTP_eof(NYTP_file ifile)617 NYTP_eof(NYTP_file ifile) {
618     ERRNO_PROBE;
619 #ifdef HAS_ZLIB
620     if (FILE_STATE(ifile) == NYTP_FILE_INFLATE) {
621         return ifile->zlib_at_eof;
622     }
623 #endif
624     {
625         dNFTHX(ifile);
626         return feof(ifile->file);
627     }
628 }
629 
630 const char *
NYTP_fstrerror(NYTP_file file)631 NYTP_fstrerror(NYTP_file file) {
632 #ifdef HAS_ZLIB
633     if (FILE_STATE(file) == NYTP_FILE_DEFLATE || FILE_STATE(file) == NYTP_FILE_INFLATE) {
634         return file->zs.msg;
635     }
636 #endif
637     {
638         dNFTHX(file);
639         return strerror(errno);
640     }
641 }
642 
643 int
NYTP_close(NYTP_file file,int discard)644 NYTP_close(NYTP_file file, int discard) {
645     FILE *raw_file = file->file;
646     int result;
647     dNFTHX(file);
648     ERRNO_PROBE;
649 
650 #ifdef HAS_ZLIB
651     if (!discard && FILE_STATE(file) == NYTP_FILE_DEFLATE) {
652         const double ratio = file->zs.total_in / (double) file->zs.total_out;
653         flush_output(file, Z_FINISH);
654         fprintf(raw_file, "#\n"
655                 "# Compressed %lu bytes to %lu, ratio %f:1, data shrunk by %f%%\n",
656                 (long)file->zs.total_in, (long)file->zs.total_out, ratio,
657                 100 * (1 - 1 / ratio));
658     }
659 
660     if (FILE_STATE(file) == NYTP_FILE_DEFLATE) {
661         int status = deflateEnd(&(file->zs));
662         if (status != Z_OK) {
663             if (discard && status == Z_DATA_ERROR) {
664                 /* deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
665                    stream state was inconsistent, Z_DATA_ERROR if the stream
666                    was freed prematurely (some input or output was discarded).
667                 */
668             } else {
669                 croak("deflateEnd failed, error %d (%s) in %d", status,
670                       file->zs.msg, getpid());
671             }
672         }
673     }
674     else if (FILE_STATE(file) == NYTP_FILE_INFLATE) {
675         int err = inflateEnd(&(file->zs));
676         if (err != Z_OK) {
677             croak("inflateEnd failed, error %d (%s)", err, file->zs.msg);
678         }
679     }
680 #endif
681 
682     Safefree(file);
683 
684     result = ferror(raw_file) ? errno : 0;
685 
686     if (discard) {
687         /* close the underlying fd first so any buffered data gets discarded
688          * when fclose is called below */
689         close(fileno(raw_file));
690     }
691 
692     if (result || discard) {
693         /* Something has already gone wrong, so try to preserve its error */
694         fclose(raw_file);
695         return result;
696     }
697     return fclose(raw_file) == 0 ? 0 : errno;
698 }
699 
700 
701 /* ====== Low-level element I/O functions ====== */
702 
703 /**
704  * Output an integer in bytes, optionally preceded by a tag. Use the special tag
705  * NYTP_TAG_NO_TAG to suppress the tag output. A wrapper macro output_u32(fh, i)
706  * does this for you.
707  * "In bytes" means output the number in binary, using the least number of bytes
708  * possible.  All numbers are positive. Use sign slot as a marker
709  */
710 static size_t
output_tag_u32(NYTP_file file,unsigned char tag,U32 i)711 output_tag_u32(NYTP_file file, unsigned char tag, U32 i)
712 {
713     U8 buffer[6];
714     U8 *p = buffer;
715 
716     if (tag != NYTP_TAG_NO_TAG)
717         *p++ = tag;
718 
719     /* general case. handles all integers */
720     if (i < 0x80) {                               /* < 8 bits */
721         *p++ = (U8)i;
722     }
723     else if (i < 0x4000) {                        /* < 16 bits */
724         *p++ = (U8)((i >> 8) | 0x80);
725         *p++ = (U8)i;
726     }
727     else if (i < 0x200000) {                      /* < 24 bits */
728         *p++ = (U8)((i >> 16) | 0xC0);
729         *p++ = (U8)(i >> 8);
730         *p++ = (U8)i;
731     }
732     else if (i < 0x10000000) {                    /* < 32 bits */
733         *p++ = (U8)((i >> 24) | 0xE0);
734         *p++ = (U8)(i >> 16);
735         *p++ = (U8)(i >> 8);
736         *p++ = (U8)i;
737     }
738     else {                                        /* need all the bytes. */
739         *p++ = 0xFF;
740         *p++ = (U8)(i >> 24);
741         *p++ = (U8)(i >> 16);
742         *p++ = (U8)(i >> 8);
743         *p++ = (U8)i;
744     }
745     return NYTP_write(file, buffer, p - buffer);
746 }
747 
748 static size_t
output_tag_i32(NYTP_file file,unsigned char tag,I32 i)749 output_tag_i32(NYTP_file file, unsigned char tag, I32 i)
750 {
751     return output_tag_u32(file, tag, *( (U32*) &i ) );
752 }
753 
754 #define     output_u32(fh, i)   output_tag_u32((fh), NYTP_TAG_NO_TAG, (i))
755 #define     output_i32(fh, i)   output_tag_i32((fh), NYTP_TAG_NO_TAG, (i))
756 
757 
758 /**
759  * Read an integer by decompressing the next 1 to 4 bytes of binary into a 32-
760  * bit integer. See output_int() for the compression details.
761  */
762 U32
read_u32(NYTP_file ifile)763 read_u32(NYTP_file ifile)
764 {
765     unsigned char d;
766     U32 newint;
767 
768     NYTP_read(ifile, &d, sizeof(d), "integer prefix");
769 
770     if (d < 0x80) {                               /* < 8 bits */
771         newint = d;
772     }
773     else {
774         unsigned char buffer[4];
775         unsigned char *p = buffer;
776         unsigned int length;
777 
778         if (d < 0xC0) {                          /* < 16 bits */
779             newint = d & 0x7F;
780             length = 1;
781         }
782         else if (d < 0xE0) {                     /* < 24 bits */
783             newint = d & 0x1F;
784             length = 2;
785         }
786         else if (d < 0xFF) {                     /* < 32 bits */
787             newint = d & 0xF;
788             length = 3;
789         }
790         else { /* d == 0xFF */                   /* = 32 bits */
791             newint = 0;
792             length = 4;
793         }
794         NYTP_read(ifile, buffer, length, "integer");
795         while (length--) {
796             newint <<= 8;
797             newint |= *p++;
798         }
799     }
800     return newint;
801 }
802 
803 I32
read_i32(NYTP_file ifile)804 read_i32(NYTP_file ifile) {
805     U32 u = read_u32(ifile);
806     return *( (I32*)&u );
807 }
808 
809 
810 static size_t
output_str(NYTP_file file,const char * str,I32 len)811 output_str(NYTP_file file, const char *str, I32 len) {    /* negative len signifies utf8 */
812     unsigned char tag = NYTP_TAG_STRING;
813     size_t retval;
814     size_t total;
815 
816     if (len < 0) {
817         tag = NYTP_TAG_STRING_UTF8;
818         len = -len;
819     }
820 
821     total = retval = output_tag_u32(file, tag, len);
822     if (retval <= 0)
823         return retval;
824 
825     if (len) {
826         total += retval = NYTP_write(file, str, len);
827         if (retval <= 0)
828             return retval;
829     }
830 
831     return total;
832 }
833 
834 
835 /**
836  * Output a double precision float via a simple binary write of the memory.
837  * (Minor portbility issues are seen as less important than speed and space.)
838  */
839 size_t
output_nv(NYTP_file file,NV nv)840 output_nv(NYTP_file file, NV nv)
841 {
842     return NYTP_write(file, (unsigned char *)&nv, sizeof(NV));
843 }
844 
845 /**
846  * Read an NV by simple byte copy to memory
847  */
848 NV
read_nv(NYTP_file ifile)849 read_nv(NYTP_file ifile)
850 {
851     NV nv;
852     /* no error checking on the assumption that a later token read will
853      * detect the error/eof condition
854      */
855     NYTP_read(ifile, (unsigned char *)&nv, sizeof(NV), "float");
856     return nv;
857 }
858 
859 
860 /* ====== Higher-level protocol I/O functions ====== */
861 
862 size_t
NYTP_write_header(NYTP_file ofile,U32 major,U32 minor)863 NYTP_write_header(NYTP_file ofile, U32 major, U32 minor)
864 {
865     return NYTP_printf(ofile, "NYTProf %u %u\n", major, minor);
866 }
867 
868 size_t
NYTP_write_comment(NYTP_file ofile,const char * format,...)869 NYTP_write_comment(NYTP_file ofile, const char *format, ...) {
870     size_t retval;
871     size_t retval2;
872     va_list args;
873     ERRNO_PROBE;
874 
875     retval = NYTP_write(ofile, "#", 1);
876     if (retval != 1)
877         return retval;
878 
879     va_start(args, format);
880 
881     if(strEQ(format, "%s")) {
882         const char * const s = va_arg(args, char*);
883         STRLEN len = strlen(s);
884         retval = NYTP_write(ofile, s, len);
885     } else {
886         CROAK_IF_NOT_STDIO(ofile, "NYTP_printf");
887         {
888             dNFTHX(ofile);
889             retval = vfprintf(ofile->file, format, args);
890         }
891     }
892     va_end(args);
893 
894     retval2 = NYTP_write(ofile, "\n", 1);
895     if (retval2 != 1)
896         return retval2;
897 
898     return retval + 2;
899 }
900 
901 static size_t
NYTP_write_plain_kv(NYTP_file ofile,const char prefix,const char * key,size_t key_len,const char * value,size_t value_len)902 NYTP_write_plain_kv(NYTP_file ofile, const char prefix,
903                             const char *key, size_t key_len,
904                             const char *value, size_t value_len)
905 {
906     size_t total;
907     size_t retval;
908 
909     total = retval = NYTP_write(ofile, &prefix, 1);
910     if (retval != 1)
911         return retval;
912 
913     total += retval = NYTP_write(ofile, key, key_len);
914     if (retval != key_len)
915         return retval;
916 
917     total += retval = NYTP_write(ofile, "=", 1);
918     if (retval != 1)
919         return retval;
920 
921     total += retval = NYTP_write(ofile, value, value_len);
922     if (retval != value_len)
923         return retval;
924 
925     total += retval = NYTP_write(ofile, "\n", 1);
926     if (retval != 1)
927         return retval;
928 
929     return total;
930 }
931 
932 size_t
NYTP_write_attribute_string(NYTP_file ofile,const char * key,size_t key_len,const char * value,size_t value_len)933 NYTP_write_attribute_string(NYTP_file ofile,
934                             const char *key, size_t key_len,
935                             const char *value, size_t value_len)
936 {
937     return NYTP_write_plain_kv(ofile, ':', key, key_len, value, value_len);
938 }
939 
940 #ifndef CHAR_BIT
941 #  define CHAR_BIT          8
942 #endif
943 #define LOG_2_OVER_LOG_10   0.30103
944 
945 size_t
NYTP_write_attribute_unsigned(NYTP_file ofile,const char * key,size_t key_len,unsigned long value)946 NYTP_write_attribute_unsigned(NYTP_file ofile, const char *key,
947                               size_t key_len, unsigned long value)
948 {
949     /* 3: 1 for rounding errors, 1 for the '\0'  */
950     char buffer[(int)(sizeof (unsigned long) * CHAR_BIT * LOG_2_OVER_LOG_10 + 3)];
951     const size_t len = my_snprintf(buffer, sizeof(buffer), "%lu", value);
952 
953     return NYTP_write_attribute_string(ofile, key, key_len, buffer, len);
954 }
955 
956 size_t
NYTP_write_attribute_signed(NYTP_file ofile,const char * key,size_t key_len,long value)957 NYTP_write_attribute_signed(NYTP_file ofile, const char *key,
958                             size_t key_len, long value)
959 {
960     /* 3: 1 for rounding errors, 1 for the sign, 1 for the '\0'  */
961     char buffer[(int)(sizeof (long) * CHAR_BIT * LOG_2_OVER_LOG_10 + 3)];
962     const size_t len = my_snprintf(buffer, sizeof(buffer), "%ld", value);
963 
964     return NYTP_write_attribute_string(ofile, key, key_len, buffer, len);
965 }
966 
967 size_t
NYTP_write_attribute_nv(NYTP_file ofile,const char * key,size_t key_len,NV value)968 NYTP_write_attribute_nv(NYTP_file ofile, const char *key,
969                             size_t key_len, NV value)
970 {
971     char buffer[NV_DIG+20]; /* see Perl_sv_2pv_flags */
972     const size_t len = my_snprintf(buffer, sizeof(buffer), "%" NVgf, value);
973 
974     return NYTP_write_attribute_string(ofile, key, key_len, buffer, len);
975 }
976 
977 /* options */
978 
979 size_t
NYTP_write_option_pv(NYTP_file ofile,const char * key,const char * value,size_t value_len)980 NYTP_write_option_pv(NYTP_file ofile,
981                     const char *key,
982                     const char *value, size_t value_len)
983 {
984     return NYTP_write_plain_kv(ofile, '!', key, strlen(key), value, value_len);
985 }
986 
987 size_t
NYTP_write_option_iv(NYTP_file ofile,const char * key,IV value)988 NYTP_write_option_iv(NYTP_file ofile, const char *key, IV value)
989 {
990     /* 3: 1 for rounding errors, 1 for the sign, 1 for the '\0'  */
991     char buffer[(int)(sizeof (IV) * CHAR_BIT * LOG_2_OVER_LOG_10 + 3)];
992     const size_t len = my_snprintf(buffer, sizeof(buffer), "%" IVdf, value);
993 
994     return NYTP_write_option_pv(ofile, key, buffer, len);
995 }
996 
997 /* other */
998 
999 #ifdef HAS_ZLIB
1000 
1001 size_t
NYTP_start_deflate_write_tag_comment(NYTP_file ofile,int compression_level)1002 NYTP_start_deflate_write_tag_comment(NYTP_file ofile, int compression_level) {
1003     const unsigned char tag = NYTP_TAG_START_DEFLATE;
1004     size_t total;
1005     size_t retval;
1006 
1007     total = retval = NYTP_write_comment(ofile, "Compressed at level %d with zlib %s",
1008                                         compression_level, zlibVersion());
1009 
1010     if (retval < 1)
1011         return retval;
1012 
1013     total += retval = NYTP_write(ofile, &tag, sizeof(tag));
1014     if (retval < 1)
1015         return retval;
1016 
1017     NYTP_start_deflate(ofile, compression_level);
1018 
1019     return total;
1020 }
1021 
1022 #endif
1023 
1024 size_t
NYTP_write_process_start(NYTP_file ofile,U32 pid,U32 ppid,NV time_of_day)1025 NYTP_write_process_start(NYTP_file ofile, U32 pid, U32 ppid,
1026                          NV time_of_day)
1027 {
1028     size_t total;
1029     size_t retval;
1030 
1031     total = retval = output_tag_u32(ofile, NYTP_TAG_PID_START, pid);
1032     if (retval < 1)
1033         return retval;
1034 
1035     total += retval = output_u32(ofile, ppid);
1036     if (retval < 1)
1037         return retval;
1038 
1039     total += retval = output_nv(ofile, time_of_day);
1040     if (retval < 1)
1041         return retval;
1042 
1043     return total;
1044 }
1045 
1046 size_t
NYTP_write_process_end(NYTP_file ofile,U32 pid,NV time_of_day)1047 NYTP_write_process_end(NYTP_file ofile, U32 pid, NV time_of_day)
1048 {
1049     size_t total;
1050     size_t retval;
1051 
1052     total = retval = output_tag_u32(ofile, NYTP_TAG_PID_END, pid);
1053     if (retval < 1)
1054         return retval;
1055 
1056     total += retval = output_nv(ofile, time_of_day);
1057     if (retval < 1)
1058         return retval;
1059 
1060     return total;
1061 }
1062 
1063 size_t
NYTP_write_sawampersand(NYTP_file ofile,U32 fid,U32 line)1064 NYTP_write_sawampersand(NYTP_file ofile, U32 fid, U32 line)
1065 {
1066     size_t total;
1067     size_t retval;
1068 
1069     total = retval = NYTP_write_attribute_unsigned(ofile, STR_WITH_LEN("sawampersand_fid"),  fid);
1070     if (retval < 1)
1071         return retval;
1072 
1073     total += retval = NYTP_write_attribute_unsigned(ofile, STR_WITH_LEN("sawampersand_line"), line);
1074     if (retval < 1)
1075         return retval;
1076 
1077     return total;
1078 }
1079 
1080 size_t
NYTP_write_new_fid(NYTP_file ofile,U32 id,U32 eval_fid,U32 eval_line_num,U32 flags,U32 size,U32 mtime,const char * name,I32 len)1081 NYTP_write_new_fid(NYTP_file ofile, U32 id, U32 eval_fid,
1082                    U32 eval_line_num, U32 flags,
1083                    U32 size, U32 mtime,
1084                    const char *name, I32 len)
1085 {
1086     size_t total;
1087     size_t retval;
1088 
1089     total = retval = output_tag_u32(ofile, NYTP_TAG_NEW_FID, id);
1090     if (retval < 1)
1091         return retval;
1092 
1093     total += retval = output_u32(ofile, eval_fid);
1094     if (retval < 1)
1095         return retval;
1096 
1097     total += retval = output_u32(ofile, eval_line_num);
1098     if (retval < 1)
1099         return retval;
1100 
1101     total += retval = output_u32(ofile, flags);
1102     if (retval < 1)
1103         return retval;
1104 
1105     total += retval = output_u32(ofile, size);
1106     if (retval < 1)
1107         return retval;
1108 
1109     total += retval = output_u32(ofile, mtime);
1110     if (retval < 1)
1111         return retval;
1112 
1113     total += retval = output_str(ofile, name, len);
1114     if (retval < 1)
1115         return retval;
1116 
1117     return total;
1118 }
1119 
1120 static size_t
write_time_common(NYTP_file ofile,unsigned char tag,I32 elapsed,U32 overflow,U32 fid,U32 line)1121 write_time_common(NYTP_file ofile, unsigned char tag, I32 elapsed, U32 overflow,
1122                   U32 fid, U32 line)
1123 {
1124     size_t total;
1125     size_t retval;
1126 
1127     if (overflow) {
1128         dNFTHX(ofile);
1129         /* XXX needs protocol change to output a new time-overflow tag */
1130         fprintf(stderr, "profile time overflow of %lu seconds discarded!\n",
1131             (unsigned long)overflow);
1132     }
1133 
1134     total = retval = output_tag_i32(ofile, tag, elapsed);
1135     if (retval < 1)
1136         return retval;
1137 
1138     total += retval = output_u32(ofile, fid);
1139     if (retval < 1)
1140         return retval;
1141 
1142     total += retval = output_u32(ofile, line);
1143     if (retval < 1)
1144         return retval;
1145 
1146     return total;
1147 }
1148 
1149 size_t
NYTP_write_time_block(NYTP_file ofile,I32 elapsed,U32 overflow,U32 fid,U32 line,U32 last_block_line,U32 last_sub_line)1150 NYTP_write_time_block(NYTP_file ofile, I32 elapsed, U32 overflow,
1151     U32 fid, U32 line, U32 last_block_line, U32 last_sub_line)
1152 {
1153     size_t total;
1154     size_t retval;
1155 
1156     total = retval = write_time_common(ofile, NYTP_TAG_TIME_BLOCK, elapsed, overflow,
1157                                        fid, line);
1158     if (retval < 1)
1159         return retval;
1160 
1161     total += retval = output_u32(ofile, last_block_line);
1162     if (retval < 1)
1163         return retval;
1164 
1165     total += retval = output_u32(ofile, last_sub_line);
1166     if (retval < 1)
1167         return retval;
1168 
1169     return total;
1170 }
1171 
1172 size_t
NYTP_write_time_line(NYTP_file ofile,I32 elapsed,U32 overflow,U32 fid,U32 line)1173 NYTP_write_time_line(NYTP_file ofile, I32 elapsed, U32 overflow,
1174     U32 fid, U32 line)
1175 {
1176     return write_time_common(ofile, NYTP_TAG_TIME_LINE, elapsed, overflow, fid, line);
1177 }
1178 
1179 
1180 size_t
NYTP_write_call_entry(NYTP_file ofile,U32 caller_fid,U32 caller_line)1181 NYTP_write_call_entry(NYTP_file ofile, U32 caller_fid, U32 caller_line)
1182 {
1183     size_t total;
1184     size_t retval;
1185 
1186     total = retval = output_tag_u32(ofile, NYTP_TAG_SUB_ENTRY, caller_fid);
1187     if (retval < 1)
1188         return retval;
1189 
1190     total += retval = output_u32(ofile, caller_line);
1191     if (retval < 1)
1192         return retval;
1193 
1194     return total;
1195 }
1196 
1197 size_t
NYTP_write_call_return(NYTP_file ofile,U32 prof_depth,const char * called_subname_pv,NV incl_subr_ticks,NV excl_subr_ticks)1198 NYTP_write_call_return(NYTP_file ofile, U32 prof_depth, const char *called_subname_pv,
1199     NV incl_subr_ticks, NV excl_subr_ticks)
1200 {
1201     size_t total;
1202     size_t retval;
1203 
1204     total = retval = output_tag_u32(ofile, NYTP_TAG_SUB_RETURN, prof_depth);
1205     if (retval < 1)
1206         return retval;
1207 
1208     total += retval = output_nv(ofile, incl_subr_ticks);
1209     if (retval < 1)
1210         return retval;
1211 
1212     total += retval = output_nv(ofile, excl_subr_ticks);
1213     if (retval < 1)
1214         return retval;
1215 
1216     if (!called_subname_pv)
1217         called_subname_pv = "(null)";
1218     total += retval = output_str(ofile, called_subname_pv, strlen(called_subname_pv));
1219     if (retval < 1)
1220         return retval;
1221 
1222     return total;
1223 }
1224 
1225 
1226 size_t
NYTP_write_sub_info(NYTP_file ofile,U32 fid,const char * name,I32 len,U32 first_line,U32 last_line)1227 NYTP_write_sub_info(NYTP_file ofile, U32 fid,
1228                     const char *name, I32 len,
1229                     U32 first_line, U32 last_line)
1230 {
1231     size_t total;
1232     size_t retval;
1233 
1234     total = retval = output_tag_u32(ofile, NYTP_TAG_SUB_INFO, fid);
1235     if (retval < 1)
1236         return retval;
1237 
1238     total += retval = output_str(ofile, name, (I32)len);
1239     if (retval < 1)
1240         return retval;
1241 
1242     total += retval = output_u32(ofile, first_line);
1243     if (retval < 1)
1244         return retval;
1245 
1246     total += retval = output_u32(ofile, last_line);
1247     if (retval < 1)
1248         return retval;
1249 
1250     return total;
1251 }
1252 
1253 size_t
NYTP_write_sub_callers(NYTP_file ofile,U32 fid,U32 line,const char * caller_name,I32 caller_name_len,U32 count,NV incl_rtime,NV excl_rtime,NV reci_rtime,U32 depth,const char * called_name,I32 called_name_len)1254 NYTP_write_sub_callers(NYTP_file ofile, U32 fid, U32 line,
1255                        const char *caller_name, I32 caller_name_len,
1256                        U32 count, NV incl_rtime, NV excl_rtime,
1257                        NV reci_rtime, U32 depth,
1258                        const char *called_name, I32 called_name_len)
1259 {
1260     size_t total;
1261     size_t retval;
1262 
1263     total = retval = output_tag_u32(ofile, NYTP_TAG_SUB_CALLERS, fid);
1264     if (retval < 1)
1265         return retval;
1266 
1267     total += retval = output_u32(ofile, line);
1268     if (retval < 1)
1269         return retval;
1270 
1271     total += retval = output_str(ofile, caller_name, caller_name_len);
1272     if (retval < 1)
1273         return retval;
1274 
1275     total += retval = output_u32(ofile, count);
1276     if (retval < 1)
1277         return retval;
1278 
1279     total += retval = output_nv(ofile, incl_rtime);
1280     if (retval < 1)
1281         return retval;
1282 
1283     total += retval = output_nv(ofile, excl_rtime);
1284     if (retval < 1)
1285         return retval;
1286 
1287     total += retval = output_nv(ofile, reci_rtime);
1288     if (retval < 1)
1289         return retval;
1290 
1291     total += retval = output_u32(ofile, depth);
1292     if (retval < 1)
1293         return retval;
1294 
1295     total += retval = output_str(ofile, called_name, called_name_len);
1296     if (retval < 1)
1297         return retval;
1298 
1299     return total;
1300 }
1301 
1302 size_t
NYTP_write_src_line(NYTP_file ofile,U32 fid,U32 line,const char * text,I32 text_len)1303 NYTP_write_src_line(NYTP_file ofile, U32 fid,
1304                     U32 line, const char *text, I32 text_len)
1305 {
1306     size_t total;
1307     size_t retval;
1308 
1309     total = retval = output_tag_u32(ofile, NYTP_TAG_SRC_LINE, fid);
1310     if (retval < 1)
1311         return retval;
1312 
1313     total += retval = output_u32(ofile, line);
1314     if (retval < 1)
1315         return retval;
1316 
1317     total += retval = output_str(ofile, text, text_len);
1318     if (retval < 1)
1319         return retval;
1320 
1321     return total;
1322 }
1323 
1324 size_t
NYTP_write_discount(NYTP_file ofile)1325 NYTP_write_discount(NYTP_file ofile)
1326 {
1327     const unsigned char tag = NYTP_TAG_DISCOUNT;
1328     return NYTP_write(ofile, &tag, sizeof(tag));
1329 }
1330 
1331 
1332 MODULE = Devel::NYTProf::FileHandle     PACKAGE = Devel::NYTProf::FileHandle    PREFIX = NYTP_
1333 
1334 PROTOTYPES: DISABLE
1335 
1336 void
1337 open(pathname, mode)
1338 char *pathname
1339 char *mode
1340     PREINIT:
1341         NYTP_file fh = NYTP_open(pathname, mode);
1342         SV *object;
1343     PPCODE:
1344         if(!fh)
1345             XSRETURN(0);
1346         object = newSV(0);
1347         sv_usepvn(object, (char *) fh, sizeof(struct NYTP_file_t));
1348         ST(0) = sv_bless(sv_2mortal(newRV_noinc(object)), gv_stashpvs("Devel::NYTProf::FileHandle", GV_ADD));
1349         XSRETURN(1);
1350 
1351 int
1352 DESTROY(handle)
1353 NYTP_file handle
1354     ALIAS:
1355         close = 1
1356     PREINIT:
1357         SV *guts;
1358     CODE:
1359 	guts = SvRV(ST(0));
1360         PERL_UNUSED_VAR(ix);
1361         RETVAL = NYTP_close(handle, 0);
1362         SvPV_set(guts, NULL);
1363         SvLEN_set(guts, 0);
1364     OUTPUT:
1365         RETVAL
1366 
1367 size_t
1368 write(handle, string)
1369 NYTP_file handle
1370 SV *string
1371     PREINIT:
1372         STRLEN len;
1373         char *p;
1374     CODE:
1375         p = SvPVbyte(string, len);
1376         RETVAL = NYTP_write(handle, p, len);
1377     OUTPUT:
1378         RETVAL
1379 
1380 #ifdef HAS_ZLIB
1381 
1382 void
1383 NYTP_start_deflate(handle, compression_level = 6)
1384 NYTP_file handle
1385 int compression_level
1386 
1387 void
1388 NYTP_start_deflate_write_tag_comment(handle, compression_level = 6)
1389 NYTP_file handle
1390 int compression_level
1391 
1392 #endif
1393 
1394 size_t
1395 NYTP_write_comment(handle, comment)
1396 NYTP_file handle
1397 char *comment
1398     CODE:
1399         RETVAL = NYTP_write_comment(handle, "%s", comment);
1400     OUTPUT:
1401         RETVAL
1402 
1403 size_t
1404 NYTP_write_attribute(handle, key, value)
1405 NYTP_file handle
1406 SV *key
1407 SV *value
1408     PREINIT:
1409         STRLEN key_len;
1410         const char *const key_p = SvPVbyte(key, key_len);
1411         STRLEN value_len;
1412         const char *const value_p = SvPVbyte(value, value_len);
1413     CODE:
1414         RETVAL = NYTP_write_attribute_string(handle, key_p, key_len, value_p, value_len);
1415     OUTPUT:
1416         RETVAL
1417 
1418 size_t
1419 NYTP_write_option(handle, key, value)
1420 NYTP_file handle
1421 SV *key
1422 SV *value
1423     PREINIT:
1424         STRLEN key_len;
1425         const char *const key_p = SvPVbyte(key, key_len);
1426         STRLEN value_len;
1427         const char *const value_p = SvPVbyte(value, value_len);
1428     CODE:
1429         RETVAL = NYTP_write_option_pv(handle, key_p, value_p, value_len);
1430     OUTPUT:
1431         RETVAL
1432 
1433 size_t
1434 NYTP_write_process_start(handle, pid, ppid, time_of_day)
1435 NYTP_file handle
1436 U32 pid
1437 U32 ppid
1438 NV time_of_day
1439 
1440 size_t
1441 NYTP_write_process_end(handle, pid, time_of_day)
1442 NYTP_file handle
1443 U32 pid
1444 NV time_of_day
1445 
1446 size_t
1447 NYTP_write_new_fid(handle, id, eval_fid, eval_line_num, flags, size, mtime, name)
1448 NYTP_file handle
1449 U32 id
1450 U32 eval_fid
1451 int eval_line_num
1452 U32 flags
1453 U32 size
1454 U32 mtime
1455 SV *name
1456     PREINIT:
1457         STRLEN len;
1458         const char *const p = SvPV(name, len);
1459     CODE:
1460         RETVAL = NYTP_write_new_fid(handle, id, eval_fid, eval_line_num,
1461                                     flags, size, mtime, p,
1462                                     SvUTF8(name) ? -(I32)len : (I32)len );
1463     OUTPUT:
1464         RETVAL
1465 
1466 size_t
1467 NYTP_write_time_block(handle, elapsed, overflow, fid, line, last_block_line, last_sub_line)
1468 NYTP_file handle
1469 U32 elapsed
1470 U32 overflow
1471 U32 fid
1472 U32 line
1473 U32 last_block_line
1474 U32 last_sub_line
1475 
1476 size_t
1477 NYTP_write_time_line(handle, elapsed, overflow, fid, line)
1478 NYTP_file handle
1479 U32 elapsed
1480 U32 overflow
1481 U32 fid
1482 U32 line
1483 
1484 size_t
1485 NYTP_write_call_entry(handle, caller_fid, caller_line)
1486 NYTP_file handle
1487 U32 caller_fid
1488 U32 caller_line
1489 
1490 size_t
1491 NYTP_write_call_return(handle, prof_depth, called_subname_pv, incl_subr_ticks, excl_subr_ticks)
1492 NYTP_file handle
1493 U32 prof_depth
1494 const char *called_subname_pv
1495 NV incl_subr_ticks
1496 NV excl_subr_ticks
1497 
1498 size_t
1499 NYTP_write_sub_info(handle, fid, name, first_line, last_line)
1500 NYTP_file handle
1501 U32 fid
1502 SV *name
1503 U32 first_line
1504 U32 last_line
1505     PREINIT:
1506         STRLEN len;
1507         const char *const p = SvPV(name, len);
1508     CODE:
1509         RETVAL = NYTP_write_sub_info(handle, fid,
1510                                      p, SvUTF8(name) ? -(I32)len : (I32)len,
1511                                      first_line, last_line);
1512     OUTPUT:
1513         RETVAL
1514 
1515 size_t
1516 NYTP_write_sub_callers(handle, fid, line, caller, count, incl_rtime, excl_rtime, reci_rtime, depth, called_sub)
1517 NYTP_file handle
1518 U32 fid
1519 U32 line
1520 SV *caller
1521 U32 count
1522 NV incl_rtime
1523 NV excl_rtime
1524 NV reci_rtime
1525 U32 depth
1526 SV *called_sub
1527     PREINIT:
1528         STRLEN caller_len;
1529         const char *const caller_p = SvPV(caller, caller_len);
1530         STRLEN called_len;
1531         const char *const called_p = SvPV(called_sub, called_len);
1532     CODE:
1533         RETVAL = NYTP_write_sub_callers(handle, fid, line, caller_p,
1534                                         SvUTF8(caller) ? -(I32)caller_len : (I32)caller_len,
1535                                         count, incl_rtime, excl_rtime,
1536                                         reci_rtime, depth, called_p,
1537                                         SvUTF8(called_sub) ? -(I32)called_len : (I32)called_len);
1538     OUTPUT:
1539         RETVAL
1540 
1541 size_t
1542 NYTP_write_src_line(handle, fid,  line, text)
1543 NYTP_file handle
1544 U32 fid
1545 U32 line
1546 SV *text
1547     PREINIT:
1548         STRLEN len;
1549         const char *const p = SvPV(text, len);
1550     CODE:
1551         RETVAL = NYTP_write_src_line(handle, fid, line,
1552                                      p, SvUTF8(text) ? -(I32)len : (I32)len);
1553     OUTPUT:
1554         RETVAL
1555 
1556 size_t
1557 NYTP_write_discount(handle)
1558 NYTP_file handle
1559 
1560 size_t
1561 NYTP_write_header(handle, major, minor)
1562 NYTP_file handle
1563 U32 major
1564 U32 minor
1565 
1566