blob: f2bc64378f5785a0ef917e35b366025a0bcf8dea [file] [log] [blame]
José Fonsecaa3285532011-11-27 12:32:00 +00001/**************************************************************************
2 *
3 * Copyright 2011 Jose Fonseca
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 *
24 **************************************************************************/
25
26
Jose Fonseca038054b2017-03-23 11:52:20 +000027#include <assert.h>
José Fonsecaa3285532011-11-27 12:32:00 +000028#include <string.h>
José Fonsecab1b5a382012-04-20 22:49:20 +010029#include <getopt.h>
30
José Fonsecaa3285532011-11-27 12:32:00 +000031#include <iostream>
Jose Fonseca7c5a77c2016-03-29 12:13:23 +010032#include <memory>
José Fonsecaa3285532011-11-27 12:32:00 +000033
34#include "cli.hpp"
35
Jose Fonsecada5c63c2018-02-14 11:36:14 +000036#include <brotli/encode.h>
Jose Fonseca7c5a77c2016-03-29 12:13:23 +010037#include <zlib.h> // for crc32
Jose Fonsecaa09b4172016-03-25 09:34:06 +000038
José Fonsecaa3285532011-11-27 12:32:00 +000039#include "trace_file.hpp"
Jose Fonsecace2ed372015-11-07 23:01:11 +000040#include "trace_ostream.hpp"
José Fonsecaa3285532011-11-27 12:32:00 +000041
42
Jose Fonsecae76ff4b2015-11-07 23:47:40 +000043static const char *synopsis = "Repack a trace file with different compression.";
José Fonsecaa3285532011-11-27 12:32:00 +000044
45static void
46usage(void)
47{
48 std::cout
Jose Fonsecae76ff4b2015-11-07 23:47:40 +000049 << "usage: apitrace repack [options] <in-trace-file> <out-trace-file>\n"
José Fonsecaa3285532011-11-27 12:32:00 +000050 << synopsis << "\n"
51 << "\n"
52 << "Snappy compression allows for faster replay and smaller memory footprint,\n"
53 << "at the expense of a slightly smaller compression ratio than zlib\n"
Jose Fonsecae76ff4b2015-11-07 23:47:40 +000054 << "\n"
Jose Fonsecaa09b4172016-03-25 09:34:06 +000055 << " -b,--brotli Use Brotli compression\n"
56 << " -z,--zlib Use ZLib compression\n"
José Fonsecaa3285532011-11-27 12:32:00 +000057 << "\n";
58}
59
José Fonsecab1b5a382012-04-20 22:49:20 +010060const static char *
Jose Fonsecaa09b4172016-03-25 09:34:06 +000061shortOptions = "hbz";
José Fonsecab1b5a382012-04-20 22:49:20 +010062
63const static struct option
64longOptions[] = {
65 {"help", no_argument, 0, 'h'},
Jose Fonseca796d3bc2016-03-25 22:32:25 +000066 {"brotli", optional_argument, 0, 'b'},
Jose Fonsecae76ff4b2015-11-07 23:47:40 +000067 {"zlib", no_argument, 0, 'z'},
José Fonsecab1b5a382012-04-20 22:49:20 +010068 {0, 0, 0, 0}
69};
70
Jose Fonsecae76ff4b2015-11-07 23:47:40 +000071enum Format {
72 FORMAT_SNAPPY = 0,
73 FORMAT_ZLIB,
Jose Fonsecaa09b4172016-03-25 09:34:06 +000074 FORMAT_BROTLI,
Jose Fonsecae76ff4b2015-11-07 23:47:40 +000075};
76
Jose Fonsecaa09b4172016-03-25 09:34:06 +000077
Jose Fonsecaa09b4172016-03-25 09:34:06 +000078static int
79repack_generic(trace::File *inFile, trace::OutStream *outFile)
80{
81 const size_t size = 8192;
José Fonsecaa3285532011-11-27 12:32:00 +000082 char *buf = new char[size];
83 size_t read;
84
85 while ((read = inFile->read(buf, size)) != 0) {
86 outFile->write(buf, read);
87 }
88
89 delete [] buf;
Jose Fonsecaa09b4172016-03-25 09:34:06 +000090
91 return EXIT_SUCCESS;
92}
93
94
95static int
Jose Fonseca796d3bc2016-03-25 22:32:25 +000096repack_brotli(trace::File *inFile, const char *outFileName, int quality)
Jose Fonsecaa09b4172016-03-25 09:34:06 +000097{
Jose Fonseca8dfab732018-02-14 13:32:15 +000098 BrotliEncoderState *s = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
99 if (!s) {
100 return EXIT_FAILURE;
101 }
Jose Fonsecaa09b4172016-03-25 09:34:06 +0000102
Jose Fonsecad2de4e32016-03-29 12:12:22 +0100103 // Brotli default quality is 11, but there are problems using quality
104 // higher than 9:
105 //
106 // - Some traces cause compression to be extremely slow. Possibly the same
107 // issue as https://github.com/google/brotli/issues/330
108 // - Some traces get lower compression ratio with 11 than 9. Possibly the
109 // same issue as https://github.com/google/brotli/issues/222
Jose Fonseca8dfab732018-02-14 13:32:15 +0000110 BrotliEncoderSetParameter(s, BROTLI_PARAM_QUALITY, 9);
Jose Fonsecad2de4e32016-03-29 12:12:22 +0100111
112 // The larger the window, the higher the compression ratio and
113 // decompression speeds, so choose the maximum.
Jose Fonseca8dfab732018-02-14 13:32:15 +0000114 BrotliEncoderSetParameter(s, BROTLI_PARAM_LGWIN, 24);
Jose Fonsecad2de4e32016-03-29 12:12:22 +0100115
Jose Fonseca796d3bc2016-03-25 22:32:25 +0000116 if (quality > 0) {
Jose Fonseca8dfab732018-02-14 13:32:15 +0000117 BrotliEncoderSetParameter(s, BROTLI_PARAM_QUALITY, quality);
Jose Fonseca796d3bc2016-03-25 22:32:25 +0000118 }
119
Jose Fonsecaa09b4172016-03-25 09:34:06 +0000120 FILE *fout = fopen(outFileName, "wb");
121 if (!fout) {
122 return EXIT_FAILURE;
123 }
Jose Fonseca8dfab732018-02-14 13:32:15 +0000124
125 uLong inCrc = crc32(0L, Z_NULL, 0);
126 static const size_t kFileBufferSize = 1 << 16;
127 uint8_t *input = (uint8_t *)malloc(kFileBufferSize * 2);
128 uint8_t *output = input + kFileBufferSize;
129 size_t available_in = 0;
130 const uint8_t *next_in = nullptr;
131 size_t available_out = kFileBufferSize;
132 uint8_t *next_out = output;
133 bool is_eof = false;
134 do {
135 if (available_in == 0 && !is_eof) {
136 available_in = inFile->read(input, kFileBufferSize);
137 next_in = input;
138 if (available_in == 0) {
139 is_eof = true;
140 } else {
141 crc32(inCrc, reinterpret_cast<const Bytef *>(input), available_in);
142 }
143 }
144
145 if (!BrotliEncoderCompressStream(s,
146 is_eof ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS,
147 &available_in, &next_in,
148 &available_out, &next_out, nullptr)) {
149 std::cerr << "error: failed to compress data\n";
150 return EXIT_FAILURE;
151 }
152
153 if (available_out != kFileBufferSize) {
154 size_t out_size = kFileBufferSize - available_out;
155 fwrite(output, 1, out_size, fout);
156 if (ferror(fout)) {
157 std::cerr << "error: failed to write to " << outFileName << "\n";
158 return EXIT_FAILURE;
159 }
160 available_out = kFileBufferSize;
161 next_out = output;
162 }
163 } while(!BrotliEncoderIsFinished(s));
164
Jose Fonsecaa09b4172016-03-25 09:34:06 +0000165 fclose(fout);
166
Jose Fonseca8dfab732018-02-14 13:32:15 +0000167 BrotliEncoderDestroyInstance(s);
168
169 // Do a CRC check
Jose Fonseca7c5a77c2016-03-29 12:13:23 +0100170 std::unique_ptr<trace::File> outFileIn(trace::File::createBrotli());
171 if (!outFileIn->open(outFileName)) {
172 std::cerr << "error: failed to open " << outFileName << " for reading\n";
173 return EXIT_FAILURE;
174 }
Jose Fonseca8dfab732018-02-14 13:32:15 +0000175 uLong outCrc = crc32(0L, Z_NULL, 0);
Jose Fonseca7c5a77c2016-03-29 12:13:23 +0100176 do {
Jose Fonseca8dfab732018-02-14 13:32:15 +0000177 available_in = inFile->read(input, kFileBufferSize);
178 crc32(inCrc, reinterpret_cast<const Bytef *>(input), available_in);
179 } while (available_in > 0);
180 if (inCrc != outCrc) {
Jose Fonseca7c5a77c2016-03-29 12:13:23 +0100181 std::cerr << "error: CRC mismatch reading " << outFileName << "\n";
182 return EXIT_FAILURE;
183 }
184
Jose Fonseca8dfab732018-02-14 13:32:15 +0000185 free(input);
186
Jose Fonsecaa09b4172016-03-25 09:34:06 +0000187 return EXIT_SUCCESS;
188}
189
190static int
Jose Fonseca796d3bc2016-03-25 22:32:25 +0000191repack(const char *inFileName, const char *outFileName, Format format, int quality)
Jose Fonsecaa09b4172016-03-25 09:34:06 +0000192{
193 int ret = EXIT_FAILURE;
194
195 trace::File *inFile = trace::File::createForRead(inFileName);
196 if (!inFile) {
197 return 1;
198 }
199
200 trace::OutStream *outFile = nullptr;
201 if (format == FORMAT_SNAPPY) {
202 outFile = trace::createSnappyStream(outFileName);
203 } else if (format == FORMAT_BROTLI) {
Jose Fonseca796d3bc2016-03-25 22:32:25 +0000204 ret = repack_brotli(inFile, outFileName, quality);
Jose Fonsecaa09b4172016-03-25 09:34:06 +0000205 delete inFile;
206 return ret;
207 } else if (format == FORMAT_ZLIB) {
208 outFile = trace::createZLibStream(outFileName);
209 }
210 if (outFile) {
211 ret = repack_generic(inFile, outFile);
212 delete outFile;
213 }
214
José Fonsecaa3285532011-11-27 12:32:00 +0000215 delete inFile;
216
Jose Fonsecaa09b4172016-03-25 09:34:06 +0000217 return ret;
José Fonsecaa3285532011-11-27 12:32:00 +0000218}
219
220static int
221command(int argc, char *argv[])
222{
Jose Fonsecae76ff4b2015-11-07 23:47:40 +0000223 Format format = FORMAT_SNAPPY;
José Fonsecab1b5a382012-04-20 22:49:20 +0100224 int opt;
Jose Fonseca796d3bc2016-03-25 22:32:25 +0000225 int quality = -1;
José Fonsecab1b5a382012-04-20 22:49:20 +0100226 while ((opt = getopt_long(argc, argv, shortOptions, longOptions, NULL)) != -1) {
227 switch (opt) {
228 case 'h':
José Fonsecaa3285532011-11-27 12:32:00 +0000229 usage();
230 return 0;
Jose Fonsecaa09b4172016-03-25 09:34:06 +0000231 case 'b':
232 format = FORMAT_BROTLI;
Jose Fonseca796d3bc2016-03-25 22:32:25 +0000233 if (optarg) {
234 quality = atoi(optarg);
235 }
Jose Fonsecaa09b4172016-03-25 09:34:06 +0000236 break;
Jose Fonsecae76ff4b2015-11-07 23:47:40 +0000237 case 'z':
238 format = FORMAT_ZLIB;
239 break;
José Fonsecab1b5a382012-04-20 22:49:20 +0100240 default:
José Fonseca4ce88b82013-10-11 17:24:47 -0700241 std::cerr << "error: unexpected option `" << (char)opt << "`\n";
José Fonsecaa3285532011-11-27 12:32:00 +0000242 usage();
243 return 1;
244 }
245 }
246
José Fonsecab1b5a382012-04-20 22:49:20 +0100247 if (argc != optind + 2) {
José Fonsecaa3285532011-11-27 12:32:00 +0000248 std::cerr << "error: insufficient number of arguments\n";
249 usage();
250 return 1;
251 }
252
Jose Fonseca796d3bc2016-03-25 22:32:25 +0000253 return repack(argv[optind], argv[optind + 1], format, quality);
José Fonsecaa3285532011-11-27 12:32:00 +0000254}
255
256const Command repack_command = {
257 "repack",
258 synopsis,
259 usage,
260 command
261};