cli_info: Dump per frame metrics for trace files
Added extra information to cli_info output which includes:
* container type (compression method)
* container size (the size of the data in the storage container)
* actual data size (the size of uncompressed data)
* per frame metrics which include the folowing information:
* first call_id
* last call_id
* the total amount of calls in a frame
* uncompressed frame size
Usage:
apitrace info --dump-frames /path/to/file.trace
An example output:
{
"FileName": "/path/to/file.trace",
"ContainerVersion": 6,
"ContainerType": "Snappy",
"API": "OpenGL + GLX/WGL/CGL",
"FramesCount": 1789,
"ActualDataSize": 5272672851,
"ContainerSize": 1547699157,
"Frames": [{
"FirstCallId": 0,
"LastCallId": 2231,
"TotalCalls": 2232,
"SizeInBytes": 1259924
}, {
"FirstCallId": 2232,
"LastCallId": 2251,
"TotalCalls": 20,
"SizeInBytes": 449
}, {
[...]
]}
}
This information could be useful to analyze the data distribution in a
trace file, find transition points menu/game/etc, choose better point
for a trace file trimming, and so on.
Change-Id: I7eb98a8303393af229b6afdcb3036fea873b38d1
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/apitrace/+/2272088
Tested-by: Robert Tarasov <tutankhamen@chromium.org>
Commit-Queue: Robert Tarasov <tutankhamen@chromium.org>
Reviewed-by: David Riley <davidriley@chromium.org>
diff --git a/cli/cli_info.cpp b/cli/cli_info.cpp
index 4c5fd0c..2845fb3 100644
--- a/cli/cli_info.cpp
+++ b/cli/cli_info.cpp
@@ -48,8 +48,7 @@
#include "trace_callset.hpp"
#include "trace_option.hpp"
-static trace::CallSet calls(trace::FREQUENCY_ALL);
-static const char *synopsis = "Print given trace file(s) information.";
+static const char *synopsis = "Print given trace file(s) information in JSON format";
static void
usage(void)
@@ -59,13 +58,13 @@
<< synopsis << "\n"
"\n"
" -h, --help show this help message and exit\n"
- " --json output trace file information in JSON format\n"
+ " --dump-frames dump per frame information\n"
"\n"
;
}
enum {
- JSON_OPT = CHAR_MAX + 1,
+ DUMP_FRAMES_OPT = CHAR_MAX + 1,
};
const static char *
@@ -74,7 +73,7 @@
const static struct option
longOptions[] = {
{"help", no_argument, 0, 'h'},
- {"json", no_argument, 0, JSON_OPT},
+ {"dump-frames", no_argument, 0, DUMP_FRAMES_OPT},
{0, 0, 0, 0}
};
@@ -85,18 +84,24 @@
return trace::API_NAMES[api];
}
+struct FrameEntry {
+ size_t firstCallId, lastCallId;
+ size_t totalCalls;
+ size_t sizeInBytes;
+};
+
static int
command(int argc, char *argv[])
{
- bool json = false;
+ bool flagDumpFrames = false;
int opt;
while ((opt = getopt_long(argc, argv, shortOptions, longOptions, NULL)) != -1) {
switch (opt) {
case 'h':
usage();
return 0;
- case JSON_OPT:
- json = true;
+ case DUMP_FRAMES_OPT:
+ flagDumpFrames = true;
break;
default:
std::cerr << "error: unexpected option `" << (char)opt << "`\n";
@@ -105,6 +110,9 @@
}
}
+ typedef std::vector<FrameEntry> FrameEntries;
+ FrameEntries frames;
+
for (int i = optind; i < argc; ++i) {
unsigned long framesCount = 0;
trace::API api = trace::API_UNKNOWN;
@@ -115,31 +123,68 @@
}
trace::Call *call;
+ size_t callsInFrame = 0;
+ size_t firstCallId = 0;
+ size_t frameBytesOffset = 0;
+ bool endFrame = true;
while ((call = p.parse_call())) {
+ if (flagDumpFrames) {
+ ++callsInFrame;
+ if (endFrame) {
+ firstCallId = call->no;
+ endFrame = false;
+ }
+ }
if (api == trace::API_UNKNOWN && p.api != trace::API_UNKNOWN)
- api = p.api;
- if (call->flags & trace::CALL_FLAG_END_FRAME)
- ++framesCount;
+ api = p.api;
+ if (call->flags & trace::CALL_FLAG_END_FRAME) {
+ ++framesCount;
+ if (flagDumpFrames) {
+ size_t curBytesOffset = p.dataBytesRead();
+ frames.push_back(
+ FrameEntry {
+ firstCallId,
+ call->no,
+ callsInFrame,
+ curBytesOffset-frameBytesOffset
+ }
+ );
+ frameBytesOffset = curBytesOffset;
+ endFrame = true;
+ callsInFrame = 0;
+ }
+ }
delete call;
}
- if (json) {
+ std::cout <<
+ "{" << std::endl <<
+ " \"FileName\": \"" << argv[i] << "\"," << std::endl <<
+ " \"ContainerVersion\": " << p.getVersion() << "," << std::endl <<
+ " \"ContainerType\": \"" << p.containerType() << "\"," << std::endl <<
+ " \"API\": \"" << getApiName(api) << "\"," << std::endl <<
+ " \"FramesCount\": " << framesCount << "," << std::endl <<
+ " \"ActualDataSize\": " << p.dataBytesRead() << "," << std::endl <<
+ " \"ContainerSize\": " << p.containerSizeInBytes();
+ if (flagDumpFrames) {
+ std::cout << "," << std::endl;
std::cout <<
- "{" <<
- "\"FileName\":\"" << argv[i] << "\"," <<
- "\"ContainerVersion\":" << p.getVersion() << "," <<
- "\"API\":\"" << getApiName(api) << "\"," <<
- "\"FramesCount\":" << framesCount << "" <<
- "}" << std::endl;
+ " \"Frames\": [{" << std::endl;
+ for (auto it = frames.begin(); it != frames.end(); ++it) {
+ std::cout <<
+ " \"FirstCallId\": " << it->firstCallId << "," << std::endl <<
+ " \"LastCallId\": " << it->lastCallId << "," << std::endl <<
+ " \"TotalCalls\": " << it->totalCalls << "," << std::endl <<
+ " \"SizeInBytes\": " << it->sizeInBytes << std::endl;
+ if (it != std::prev(frames.end())) {
+ std::cout << " }, {" << std::endl;
+ }
+ }
+ std::cout << " }]" << std::endl;
} else {
- std::cout <<
- std::endl <<
- "FileName: " << argv[i] << std::endl <<
- "ContainerVersion: " << p.getVersion() << std::endl <<
- "API: " << getApiName(api) << std::endl <<
- "FramesCount: " << framesCount << std::endl <<
- std::endl;
+ std::cout << std::endl;
}
+ std::cout << "}" << std::endl;
}
return 0;
diff --git a/lib/trace/trace_file.hpp b/lib/trace/trace_file.hpp
index 26b525c..5d70dc8 100644
--- a/lib/trace/trace_file.hpp
+++ b/lib/trace/trace_file.hpp
@@ -59,7 +59,16 @@
void close(void);
int getc(void);
bool skip(size_t length);
- int percentRead(void);
+ int percentRead(void) const;
+
+ // returns the size of (compressed/serialized) data in the container in bytes
+ virtual size_t containerSizeInBytes(void) const = 0;
+ // returns the amount of bytes read from the container
+ virtual size_t containerBytesRead(void) const = 0;
+ // returns the size of uncompressed data read in bytes
+ virtual size_t dataBytesRead(void) const = 0;
+ // returns the name of the continer type as a user friendly string
+ virtual const char* containerType() const = 0;
virtual bool supportsOffsets(void) const;
virtual File::Offset currentOffset(void) const;
@@ -70,7 +79,6 @@
virtual int rawGetc(void) = 0;
virtual void rawClose(void) = 0;
virtual bool rawSkip(size_t length) = 0;
- virtual int rawPercentRead(void) = 0;
protected:
bool m_isOpened = false;
@@ -99,12 +107,14 @@
return rawRead(buffer, length);
}
-inline int File::percentRead(void)
+inline int File::percentRead(void) const
{
- if (!m_isOpened) {
- return 0;
+ size_t size = containerSizeInBytes();
+ size_t read = containerBytesRead();
+ if (size) {
+ return static_cast<int>((100*read)/size);
}
- return rawPercentRead();
+ return 0;
}
inline void File::close(void)
diff --git a/lib/trace/trace_file_brotli.cpp b/lib/trace/trace_file_brotli.cpp
index 998f390..5053a45 100644
--- a/lib/trace/trace_file_brotli.cpp
+++ b/lib/trace/trace_file_brotli.cpp
@@ -51,14 +51,20 @@
virtual int rawGetc(void) override;
virtual void rawClose(void) override;
virtual bool rawSkip(size_t length) override;
- virtual int rawPercentRead(void) override;
+
+ size_t containerSizeInBytes(void) const override;
+ size_t containerBytesRead(void) const override;
+ size_t dataBytesRead(void) const override;
+ const char *containerType(void) const override;
private:
BrotliDecoderState *state;
- std::ifstream m_stream;
+ mutable std::ifstream m_stream;
static const size_t kFileBufferSize = 65536;
uint8_t input[kFileBufferSize];
const uint8_t* next_in;
size_t available_in;
+ std::streampos m_endPos = 0;
+ size_t m_dataBytesRead = 0;
};
BrotliFile::BrotliFile(void)
@@ -80,6 +86,14 @@
| std::fstream::in;
m_stream.open(filename, fmode);
+
+ if (m_stream.is_open()) {
+ m_stream.seekg(0, std::ios::end);
+ m_endPos = m_stream.tellg();
+ m_stream.seekg(0, std::ios::beg);
+ m_dataBytesRead = 0;
+ }
+
return m_stream.is_open();
}
@@ -117,6 +131,8 @@
assert(next_out - output <= length);
+ m_dataBytesRead += static_cast<size_t>(next_out - output);
+
return next_out - output;
}
@@ -139,11 +155,21 @@
return false;
}
-int BrotliFile::rawPercentRead(void)
-{
- return 0;
+size_t BrotliFile::containerSizeInBytes(void) const {
+ return static_cast<size_t>(m_endPos);
}
+size_t BrotliFile::containerBytesRead(void) const {
+ return static_cast<size_t>(m_stream.tellg());
+}
+
+size_t BrotliFile::dataBytesRead(void) const {
+ return m_dataBytesRead;
+}
+
+const char *BrotliFile::containerType(void) const {
+ return "Brotli";
+}
File * File::createBrotli(void) {
return new BrotliFile;
diff --git a/lib/trace/trace_file_snappy.cpp b/lib/trace/trace_file_snappy.cpp
index 119e414..cb72b09 100644
--- a/lib/trace/trace_file_snappy.cpp
+++ b/lib/trace/trace_file_snappy.cpp
@@ -85,7 +85,11 @@
virtual int rawGetc(void) override;
virtual void rawClose(void) override;
virtual bool rawSkip(size_t length) override;
- virtual int rawPercentRead(void) override;
+
+ size_t containerSizeInBytes(void) const override;
+ size_t containerBytesRead(void) const override;
+ size_t dataBytesRead(void) const override;
+ const char* containerType() const override;
private:
inline size_t usedCacheSize(void) const
@@ -111,7 +115,7 @@
void createCache(size_t size);
size_t readCompressedLength();
private:
- std::ifstream m_stream;
+ mutable std::ifstream m_stream;
size_t m_cacheMaxSize;
size_t m_cacheSize;
char *m_cache;
@@ -119,8 +123,9 @@
char *m_compressedCache;
- uint64_t m_currentChunkOffset;
- std::streampos m_endPos;
+ uint64_t m_currentChunkOffset = 0;
+ std::streampos m_endPos = 0;
+ size_t m_dataBytesRead = 0;
};
SnappyFile::SnappyFile(void)
@@ -155,6 +160,8 @@
m_endPos = m_stream.tellg();
m_stream.seekg(0, std::ios::beg);
+ m_dataBytesRead = 0;
+
// read the snappy file identifier
unsigned char byte1, byte2;
m_stream >> byte1;
@@ -175,6 +182,7 @@
if (freeCacheSize() >= length) {
memcpy(buffer, m_cachePtr, length);
m_cachePtr += length;
+ m_dataBytesRead += length;
} else {
size_t sizeToRead = length;
size_t offset = 0;
@@ -183,6 +191,7 @@
offset = length - sizeToRead;
memcpy((char*)buffer + offset, m_cachePtr, chunkSize);
m_cachePtr += chunkSize;
+ m_dataBytesRead += chunkSize;
sizeToRead -= chunkSize;
if (sizeToRead > 0) {
flushReadCache();
@@ -324,11 +333,13 @@
if (freeCacheSize() >= length) {
m_cachePtr += length;
+ m_dataBytesRead += length;
} else {
size_t sizeToRead = length;
while (sizeToRead) {
size_t chunkSize = std::min(freeCacheSize(), sizeToRead);
m_cachePtr += chunkSize;
+ m_dataBytesRead += chunkSize;
sizeToRead -= chunkSize;
if (sizeToRead > 0) {
flushReadCache(sizeToRead);
@@ -342,11 +353,21 @@
return true;
}
-int SnappyFile::rawPercentRead(void)
-{
- return int(100 * (double(m_stream.tellg()) / double(m_endPos)));
+size_t SnappyFile::containerSizeInBytes(void) const {
+ return static_cast<size_t>(m_endPos);
}
+size_t SnappyFile::containerBytesRead(void) const {
+ return m_currentChunkOffset;
+}
+
+size_t SnappyFile::dataBytesRead(void) const {
+ return static_cast<size_t>(m_dataBytesRead);
+}
+
+const char *SnappyFile::containerType(void) const {
+ return "Snappy";
+}
File* File::createSnappy(void) {
return new SnappyFile;
diff --git a/lib/trace/trace_file_zlib.cpp b/lib/trace/trace_file_zlib.cpp
index f0dd456..424f726 100644
--- a/lib/trace/trace_file_zlib.cpp
+++ b/lib/trace/trace_file_zlib.cpp
@@ -59,11 +59,16 @@
virtual int rawGetc(void) override;
virtual void rawClose(void) override;
virtual bool rawSkip(size_t length) override;
- virtual int rawPercentRead(void) override;
+
+ size_t containerSizeInBytes(void) const override;
+ size_t containerBytesRead(void) const override;
+ size_t dataBytesRead(void) const override;
+ const char *containerType(void) const override;
private:
int fd = 0;
gzFile m_gzFile = nullptr;
off_t m_endOffset = 0;
+ size_t m_dataBytesRead = 0;
};
ZLibFile::ZLibFile(void)
@@ -104,6 +109,7 @@
off_t loc = lseek(fd, 0, SEEK_CUR);
m_endOffset = lseek(fd, 0, SEEK_END);
lseek(fd, loc, SEEK_SET);
+ m_dataBytesRead = 0;
}
return m_gzFile != NULL;
@@ -112,6 +118,9 @@
size_t ZLibFile::rawRead(void *buffer, size_t length)
{
int ret = gzread(m_gzFile, buffer, unsigned(length));
+ if (ret > 0) {
+ m_dataBytesRead += static_cast<size_t>(ret);
+ }
return ret < 0 ? 0 : ret;
}
@@ -133,9 +142,20 @@
return false;
}
-int ZLibFile::rawPercentRead(void)
-{
- return int(100 * (lseek(fd, 0, SEEK_CUR) / m_endOffset));
+size_t ZLibFile::containerSizeInBytes(void) const {
+ return static_cast<size_t>(m_endOffset);
+}
+
+size_t ZLibFile::containerBytesRead(void) const {
+ return static_cast<size_t>(lseek(fd, 0, SEEK_CUR));
+}
+
+size_t ZLibFile::dataBytesRead(void) const {
+ return m_dataBytesRead;
+}
+
+const char *ZLibFile::containerType(void) const {
+ return "ZLib";
}
diff --git a/lib/trace/trace_parser.hpp b/lib/trace/trace_parser.hpp
index e957ca8..6791190 100644
--- a/lib/trace/trace_parser.hpp
+++ b/lib/trace/trace_parser.hpp
@@ -151,11 +151,27 @@
return properties;
}
- int percentRead()
- {
+
+ int percentRead() const {
return file->percentRead();
}
+ size_t containerSizeInBytes() const {
+ return file->containerSizeInBytes();
+ }
+
+ size_t containerBytesRead() const {
+ return file->containerBytesRead();
+ }
+
+ size_t dataBytesRead() const {
+ return file->dataBytesRead();
+ }
+
+ const char *containerType() const {
+ return file->containerType();
+ }
+
Call *scan_call() {
return parse_call(SCAN);
}