blob: ab2d225102a2021085c608e06ec40feb846b094b [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 Fonseca2b42d832018-12-20 10:38:38 +000055 << " -b,--brotli[=QUALITY] Use Brotli compression (quality " << BROTLI_MIN_QUALITY << "-" << BROTLI_MAX_QUALITY << ", default " << BROTLI_DEFAULT_QUALITY << ")\n"
Jose Fonsecad0f19ab2018-12-19 11:29:42 +000056 << " -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 Fonseca2b42d832018-12-20 10:38:38 +0000103 // Brotli default quality is 11. There used to be problems using quality
Jose Fonsecad2de4e32016-03-29 12:12:22 +0100104 // 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 Fonseca2b42d832018-12-20 10:38:38 +0000110 //
111 // but not any more.
112 BrotliEncoderSetParameter(s, BROTLI_PARAM_QUALITY, quality);
Jose Fonsecad2de4e32016-03-29 12:12:22 +0100113
114 // The larger the window, the higher the compression ratio and
115 // decompression speeds, so choose the maximum.
Jose Fonseca8dfab732018-02-14 13:32:15 +0000116 BrotliEncoderSetParameter(s, BROTLI_PARAM_LGWIN, 24);
Jose Fonsecad2de4e32016-03-29 12:12:22 +0100117
Jose Fonsecaa09b4172016-03-25 09:34:06 +0000118 FILE *fout = fopen(outFileName, "wb");
119 if (!fout) {
120 return EXIT_FAILURE;
121 }
Jose Fonseca8dfab732018-02-14 13:32:15 +0000122
123 uLong inCrc = crc32(0L, Z_NULL, 0);
124 static const size_t kFileBufferSize = 1 << 16;
125 uint8_t *input = (uint8_t *)malloc(kFileBufferSize * 2);
126 uint8_t *output = input + kFileBufferSize;
127 size_t available_in = 0;
128 const uint8_t *next_in = nullptr;
129 size_t available_out = kFileBufferSize;
130 uint8_t *next_out = output;
131 bool is_eof = false;
132 do {
133 if (available_in == 0 && !is_eof) {
134 available_in = inFile->read(input, kFileBufferSize);
135 next_in = input;
136 if (available_in == 0) {
137 is_eof = true;
138 } else {
139 crc32(inCrc, reinterpret_cast<const Bytef *>(input), available_in);
140 }
141 }
142
143 if (!BrotliEncoderCompressStream(s,
144 is_eof ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS,
145 &available_in, &next_in,
146 &available_out, &next_out, nullptr)) {
147 std::cerr << "error: failed to compress data\n";
148 return EXIT_FAILURE;
149 }
150
151 if (available_out != kFileBufferSize) {
152 size_t out_size = kFileBufferSize - available_out;
153 fwrite(output, 1, out_size, fout);
154 if (ferror(fout)) {
155 std::cerr << "error: failed to write to " << outFileName << "\n";
156 return EXIT_FAILURE;
157 }
158 available_out = kFileBufferSize;
159 next_out = output;
160 }
161 } while(!BrotliEncoderIsFinished(s));
162
Jose Fonsecaa09b4172016-03-25 09:34:06 +0000163 fclose(fout);
164
Jose Fonseca8dfab732018-02-14 13:32:15 +0000165 BrotliEncoderDestroyInstance(s);
166
167 // Do a CRC check
Jose Fonseca7c5a77c2016-03-29 12:13:23 +0100168 std::unique_ptr<trace::File> outFileIn(trace::File::createBrotli());
169 if (!outFileIn->open(outFileName)) {
170 std::cerr << "error: failed to open " << outFileName << " for reading\n";
171 return EXIT_FAILURE;
172 }
Jose Fonseca8dfab732018-02-14 13:32:15 +0000173 uLong outCrc = crc32(0L, Z_NULL, 0);
Jose Fonseca7c5a77c2016-03-29 12:13:23 +0100174 do {
Jose Fonseca8dfab732018-02-14 13:32:15 +0000175 available_in = inFile->read(input, kFileBufferSize);
176 crc32(inCrc, reinterpret_cast<const Bytef *>(input), available_in);
177 } while (available_in > 0);
178 if (inCrc != outCrc) {
Jose Fonseca7c5a77c2016-03-29 12:13:23 +0100179 std::cerr << "error: CRC mismatch reading " << outFileName << "\n";
180 return EXIT_FAILURE;
181 }
182
Jose Fonseca8dfab732018-02-14 13:32:15 +0000183 free(input);
184
Jose Fonsecaa09b4172016-03-25 09:34:06 +0000185 return EXIT_SUCCESS;
186}
187
188static int
Jose Fonseca796d3bc2016-03-25 22:32:25 +0000189repack(const char *inFileName, const char *outFileName, Format format, int quality)
Jose Fonsecaa09b4172016-03-25 09:34:06 +0000190{
191 int ret = EXIT_FAILURE;
192
193 trace::File *inFile = trace::File::createForRead(inFileName);
194 if (!inFile) {
195 return 1;
196 }
197
198 trace::OutStream *outFile = nullptr;
199 if (format == FORMAT_SNAPPY) {
200 outFile = trace::createSnappyStream(outFileName);
201 } else if (format == FORMAT_BROTLI) {
Jose Fonseca796d3bc2016-03-25 22:32:25 +0000202 ret = repack_brotli(inFile, outFileName, quality);
Jose Fonsecaa09b4172016-03-25 09:34:06 +0000203 delete inFile;
204 return ret;
205 } else if (format == FORMAT_ZLIB) {
206 outFile = trace::createZLibStream(outFileName);
207 }
208 if (outFile) {
209 ret = repack_generic(inFile, outFile);
210 delete outFile;
211 }
212
José Fonsecaa3285532011-11-27 12:32:00 +0000213 delete inFile;
214
Jose Fonsecaa09b4172016-03-25 09:34:06 +0000215 return ret;
José Fonsecaa3285532011-11-27 12:32:00 +0000216}
217
218static int
219command(int argc, char *argv[])
220{
Jose Fonsecae76ff4b2015-11-07 23:47:40 +0000221 Format format = FORMAT_SNAPPY;
José Fonsecab1b5a382012-04-20 22:49:20 +0100222 int opt;
Jose Fonseca2b42d832018-12-20 10:38:38 +0000223 int quality = BROTLI_DEFAULT_QUALITY;
José Fonsecab1b5a382012-04-20 22:49:20 +0100224 while ((opt = getopt_long(argc, argv, shortOptions, longOptions, NULL)) != -1) {
225 switch (opt) {
226 case 'h':
José Fonsecaa3285532011-11-27 12:32:00 +0000227 usage();
228 return 0;
Jose Fonsecaa09b4172016-03-25 09:34:06 +0000229 case 'b':
230 format = FORMAT_BROTLI;
Jose Fonseca796d3bc2016-03-25 22:32:25 +0000231 if (optarg) {
232 quality = atoi(optarg);
Jose Fonseca2b42d832018-12-20 10:38:38 +0000233 if (quality < BROTLI_MIN_QUALITY || quality > BROTLI_MAX_QUALITY) {
234 std::cerr << "error: brotli quality must be between " << BROTLI_MIN_QUALITY << " and " << BROTLI_MAX_QUALITY << std::endl;
235 return 1;
236 }
Jose Fonseca796d3bc2016-03-25 22:32:25 +0000237 }
Jose Fonsecaa09b4172016-03-25 09:34:06 +0000238 break;
Jose Fonsecae76ff4b2015-11-07 23:47:40 +0000239 case 'z':
240 format = FORMAT_ZLIB;
241 break;
José Fonsecab1b5a382012-04-20 22:49:20 +0100242 default:
José Fonseca4ce88b82013-10-11 17:24:47 -0700243 std::cerr << "error: unexpected option `" << (char)opt << "`\n";
José Fonsecaa3285532011-11-27 12:32:00 +0000244 usage();
245 return 1;
246 }
247 }
248
José Fonsecab1b5a382012-04-20 22:49:20 +0100249 if (argc != optind + 2) {
José Fonsecaa3285532011-11-27 12:32:00 +0000250 std::cerr << "error: insufficient number of arguments\n";
251 usage();
252 return 1;
253 }
254
Jose Fonseca796d3bc2016-03-25 22:32:25 +0000255 return repack(argv[optind], argv[optind + 1], format, quality);
José Fonsecaa3285532011-11-27 12:32:00 +0000256}
257
258const Command repack_command = {
259 "repack",
260 synopsis,
261 usage,
262 command
263};