1 /*
2  * Copyright (c) Facebook, Inc.
3  * All rights reserved.
4  *
5  * This source code is licensed under both the BSD-style license (found in the
6  * LICENSE file in the root directory of this source tree) and the GPLv2 (found
7  * in the COPYING file in the root directory of this source tree).
8  * You may select, at your option, one of the above-listed licenses.
9  */
10 
11 #include "zstdcli_trace.h"
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 
16 #include "timefn.h"
17 #include "util.h"
18 
19 #define ZSTD_STATIC_LINKING_ONLY
20 #include "../lib/zstd.h"
21 /* We depend on the trace header to avoid duplicating the ZSTD_trace struct.
22  * But, we check the version so it is compatible with dynamic linking.
23  */
24 #include "../lib/common/zstd_trace.h"
25 /* We only use macros from threading.h so it is compatible with dynamic linking */
26 #include "../lib/common/threading.h"
27 
28 #if ZSTD_TRACE
29 
30 static FILE* g_traceFile = NULL;
31 static int g_mutexInit = 0;
32 static ZSTD_pthread_mutex_t g_mutex;
33 static UTIL_time_t g_enableTime = UTIL_TIME_INITIALIZER;
34 
35 void TRACE_enable(char const* filename)
36 {
37     int const writeHeader = !UTIL_isRegularFile(filename);
38     if (g_traceFile)
39         fclose(g_traceFile);
40     g_traceFile = fopen(filename, "a");
41     if (g_traceFile && writeHeader) {
42         /* Fields:
43         * algorithm
44         * version
45         * method
46         * streaming
47         * level
48         * workers
49         * dictionary size
50         * uncompressed size
51         * compressed size
52         * duration nanos
53         * compression ratio
54         * speed MB/s
55         */
56         fprintf(g_traceFile, "Algorithm, Version, Method, Mode, Level, Workers, Dictionary Size, Uncompressed Size, Compressed Size, Duration Nanos, Compression Ratio, Speed MB/s\n");
57     }
58     g_enableTime = UTIL_getTime();
59     if (!g_mutexInit) {
60         if (!ZSTD_pthread_mutex_init(&g_mutex, NULL)) {
61             g_mutexInit = 1;
62         } else {
63             TRACE_finish();
64         }
65     }
66 }
67 
68 void TRACE_finish(void)
69 {
70     if (g_traceFile) {
71         fclose(g_traceFile);
72     }
73     g_traceFile = NULL;
74     if (g_mutexInit) {
75         ZSTD_pthread_mutex_destroy(&g_mutex);
76         g_mutexInit = 0;
77     }
78 }
79 
80 static void TRACE_log(char const* method, PTime duration, ZSTD_Trace const* trace)
81 {
82     int level = 0;
83     int workers = 0;
84     double const ratio = (double)trace->uncompressedSize / (double)trace->compressedSize;
85     double const speed = ((double)trace->uncompressedSize * 1000) / (double)duration;
86     if (trace->params) {
87         ZSTD_CCtxParams_getParameter(trace->params, ZSTD_c_compressionLevel, &level);
88         ZSTD_CCtxParams_getParameter(trace->params, ZSTD_c_nbWorkers, &workers);
89     }
90     assert(g_traceFile != NULL);
91 
92     ZSTD_pthread_mutex_lock(&g_mutex);
93     /* Fields:
94      * algorithm
95      * version
96      * method
97      * streaming
98      * level
99      * workers
100      * dictionary size
101      * uncompressed size
102      * compressed size
103      * duration nanos
104      * compression ratio
105      * speed MB/s
106      */
107     fprintf(g_traceFile,
108         "zstd, %u, %s, %s, %d, %d, %llu, %llu, %llu, %llu, %.2f, %.2f\n",
109         trace->version,
110         method,
111         trace->streaming ? "streaming" : "single-pass",
112         level,
113         workers,
114         (unsigned long long)trace->dictionarySize,
115         (unsigned long long)trace->uncompressedSize,
116         (unsigned long long)trace->compressedSize,
117         (unsigned long long)duration,
118         ratio,
119         speed);
120     ZSTD_pthread_mutex_unlock(&g_mutex);
121 }
122 
123 /**
124  * These symbols override the weak symbols provided by the library.
125  */
126 
127 ZSTD_TraceCtx ZSTD_trace_compress_begin(ZSTD_CCtx const* cctx)
128 {
129     (void)cctx;
130     if (g_traceFile == NULL)
131         return 0;
132     return (ZSTD_TraceCtx)UTIL_clockSpanNano(g_enableTime);
133 }
134 
135 void ZSTD_trace_compress_end(ZSTD_TraceCtx ctx, ZSTD_Trace const* trace)
136 {
137     PTime const beginNanos = (PTime)ctx;
138     PTime const endNanos = UTIL_clockSpanNano(g_enableTime);
139     PTime const durationNanos = endNanos > beginNanos ? endNanos - beginNanos : 0;
140     assert(g_traceFile != NULL);
141     assert(trace->version == ZSTD_VERSION_NUMBER); /* CLI version must match. */
142     TRACE_log("compress", durationNanos, trace);
143 }
144 
145 ZSTD_TraceCtx ZSTD_trace_decompress_begin(ZSTD_DCtx const* dctx)
146 {
147     (void)dctx;
148     if (g_traceFile == NULL)
149         return 0;
150     return (ZSTD_TraceCtx)UTIL_clockSpanNano(g_enableTime);
151 }
152 
153 void ZSTD_trace_decompress_end(ZSTD_TraceCtx ctx, ZSTD_Trace const* trace)
154 {
155     PTime const beginNanos = (PTime)ctx;
156     PTime const endNanos = UTIL_clockSpanNano(g_enableTime);
157     PTime const durationNanos = endNanos > beginNanos ? endNanos - beginNanos : 0;
158     assert(g_traceFile != NULL);
159     assert(trace->version == ZSTD_VERSION_NUMBER); /* CLI version must match. */
160     TRACE_log("decompress", durationNanos, trace);
161 }
162 
163 #else /* ZSTD_TRACE */
164 
165 void TRACE_enable(char const* filename)
166 {
167     (void)filename;
168 }
169 
170 void TRACE_finish(void) {}
171 
172 #endif /* ZSTD_TRACE */
173