blob: 36fa1e34fb7c2c554d4910a963c5eea61ae0c0d7 [file] [log] [blame]
tereliusee37e862017-04-28 07:48:17 -07001/*
2 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include <inttypes.h>
12#include <stdio.h>
13
14#include <fstream>
15#include <iostream>
16#include <map>
17#include <string>
18#include <tuple>
19#include <utility>
20#include <vector>
21
tereliusee37e862017-04-28 07:48:17 -070022#include "webrtc/logging/rtc_event_log/rtc_event_log.h"
Edward Lemurc20978e2017-07-06 19:44:34 +020023#include "webrtc/rtc_base/checks.h"
oprypin6e09d872017-08-31 03:21:39 -070024#include "webrtc/rtc_base/flags.h"
Edward Lemurc20978e2017-07-06 19:44:34 +020025#include "webrtc/rtc_base/ignore_wundef.h"
26#include "webrtc/rtc_base/logging.h"
tereliusee37e862017-04-28 07:48:17 -070027
28// Files generated at build-time by the protobuf compiler.
29RTC_PUSH_IGNORING_WUNDEF()
30#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
31#include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log.pb.h"
32#else
33#include "webrtc/logging/rtc_event_log/rtc_event_log.pb.h"
34#endif
35RTC_POP_IGNORING_WUNDEF()
36
37namespace {
38
oprypin6e09d872017-08-31 03:21:39 -070039DEFINE_bool(help, false, "Prints this message.");
40
tereliusee37e862017-04-28 07:48:17 -070041struct Stats {
42 int count = 0;
43 size_t total_size = 0;
44};
45
46// We are duplicating some parts of the parser here because we want to get
47// access to raw protobuf events.
48std::pair<uint64_t, bool> ParseVarInt(std::istream& stream) {
49 uint64_t varint = 0;
50 for (size_t bytes_read = 0; bytes_read < 10; ++bytes_read) {
51 // The most significant bit of each byte is 0 if it is the last byte in
52 // the varint and 1 otherwise. Thus, we take the 7 least significant bits
53 // of each byte and shift them 7 bits for each byte read previously to get
54 // the (unsigned) integer.
55 int byte = stream.get();
56 if (stream.eof()) {
57 return std::make_pair(varint, false);
58 }
kwibergee89e782017-08-09 17:22:01 -070059 RTC_DCHECK_GE(byte, 0);
60 RTC_DCHECK_LE(byte, 255);
tereliusee37e862017-04-28 07:48:17 -070061 varint |= static_cast<uint64_t>(byte & 0x7F) << (7 * bytes_read);
62 if ((byte & 0x80) == 0) {
63 return std::make_pair(varint, true);
64 }
65 }
66 return std::make_pair(varint, false);
67}
68
69bool ParseEvents(const std::string& filename,
70 std::vector<webrtc::rtclog::Event>* events) {
71 std::ifstream stream(filename, std::ios_base::in | std::ios_base::binary);
72 if (!stream.good() || !stream.is_open()) {
73 LOG(LS_WARNING) << "Could not open file for reading.";
74 return false;
75 }
76
77 const size_t kMaxEventSize = (1u << 16) - 1;
78 std::vector<char> tmp_buffer(kMaxEventSize);
79 uint64_t tag;
80 uint64_t message_length;
81 bool success;
82
83 RTC_DCHECK(stream.good());
84
85 while (1) {
86 // Check whether we have reached end of file.
87 stream.peek();
88 if (stream.eof()) {
89 return true;
90 }
91
92 // Read the next message tag. The tag number is defined as
93 // (fieldnumber << 3) | wire_type. In our case, the field number is
94 // supposed to be 1 and the wire type for an length-delimited field is 2.
95 const uint64_t kExpectedTag = (1 << 3) | 2;
96 std::tie(tag, success) = ParseVarInt(stream);
97 if (!success) {
98 LOG(LS_WARNING) << "Missing field tag from beginning of protobuf event.";
99 return false;
100 } else if (tag != kExpectedTag) {
101 LOG(LS_WARNING) << "Unexpected field tag at beginning of protobuf event.";
102 return false;
103 }
104
105 // Read the length field.
106 std::tie(message_length, success) = ParseVarInt(stream);
107 if (!success) {
108 LOG(LS_WARNING) << "Missing message length after protobuf field tag.";
109 return false;
110 } else if (message_length > kMaxEventSize) {
111 LOG(LS_WARNING) << "Protobuf message length is too large.";
112 return false;
113 }
114
115 // Read the next protobuf event to a temporary char buffer.
116 stream.read(tmp_buffer.data(), message_length);
117 if (stream.gcount() != static_cast<int>(message_length)) {
118 LOG(LS_WARNING) << "Failed to read protobuf message from file.";
119 return false;
120 }
121
122 // Parse the protobuf event from the buffer.
123 webrtc::rtclog::Event event;
124 if (!event.ParseFromArray(tmp_buffer.data(), message_length)) {
125 LOG(LS_WARNING) << "Failed to parse protobuf message.";
126 return false;
127 }
128 events->push_back(event);
129 }
130}
131
132// TODO(terelius): Should this be placed in some utility file instead?
133std::string EventTypeToString(webrtc::rtclog::Event::EventType event_type) {
134 switch (event_type) {
135 case webrtc::rtclog::Event::UNKNOWN_EVENT:
136 return "UNKNOWN_EVENT";
137 case webrtc::rtclog::Event::LOG_START:
138 return "LOG_START";
139 case webrtc::rtclog::Event::LOG_END:
140 return "LOG_END";
141 case webrtc::rtclog::Event::RTP_EVENT:
142 return "RTP_EVENT";
143 case webrtc::rtclog::Event::RTCP_EVENT:
144 return "RTCP_EVENT";
145 case webrtc::rtclog::Event::AUDIO_PLAYOUT_EVENT:
146 return "AUDIO_PLAYOUT_EVENT";
147 case webrtc::rtclog::Event::LOSS_BASED_BWE_UPDATE:
148 return "LOSS_BASED_BWE_UPDATE";
149 case webrtc::rtclog::Event::DELAY_BASED_BWE_UPDATE:
150 return "DELAY_BASED_BWE_UPDATE";
151 case webrtc::rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT:
152 return "VIDEO_RECV_CONFIG";
153 case webrtc::rtclog::Event::VIDEO_SENDER_CONFIG_EVENT:
154 return "VIDEO_SEND_CONFIG";
155 case webrtc::rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT:
156 return "AUDIO_RECV_CONFIG";
157 case webrtc::rtclog::Event::AUDIO_SENDER_CONFIG_EVENT:
158 return "AUDIO_SEND_CONFIG";
159 case webrtc::rtclog::Event::AUDIO_NETWORK_ADAPTATION_EVENT:
160 return "AUDIO_NETWORK_ADAPTATION";
161 case webrtc::rtclog::Event::BWE_PROBE_CLUSTER_CREATED_EVENT:
162 return "BWE_PROBE_CREATED";
163 case webrtc::rtclog::Event::BWE_PROBE_RESULT_EVENT:
164 return "BWE_PROBE_RESULT";
tereliusee37e862017-04-28 07:48:17 -0700165 }
166 RTC_NOTREACHED();
167 return "UNKNOWN_EVENT";
168}
169
170} // namespace
171
172// This utility will print basic information about each packet to stdout.
173// Note that parser will assert if the protobuf event is missing some required
174// fields and we attempt to access them. We don't handle this at the moment.
175int main(int argc, char* argv[]) {
176 std::string program_name = argv[0];
177 std::string usage =
178 "Tool for file usage statistics from an RtcEventLog.\n"
179 "Run " +
180 program_name +
oprypin6e09d872017-08-31 03:21:39 -0700181 " --help for usage.\n"
tereliusee37e862017-04-28 07:48:17 -0700182 "Example usage:\n" +
183 program_name + " input.rel\n";
oprypin6e09d872017-08-31 03:21:39 -0700184 if (rtc::FlagList::SetFlagsFromCommandLine(&argc, argv, true) ||
185 FLAG_help || argc != 2) {
186 std::cout << usage;
187 if (FLAG_help) {
188 rtc::FlagList::Print(nullptr, false);
189 return 0;
190 }
191 return 1;
tereliusee37e862017-04-28 07:48:17 -0700192 }
193 std::string file_name = argv[1];
194
195 std::vector<webrtc::rtclog::Event> events;
196 if (!ParseEvents(file_name, &events)) {
197 LOG(LS_ERROR) << "Failed to parse event log.";
198 return -1;
199 }
200
201 // Get file size
202 FILE* file = fopen(file_name.c_str(), "rb");
203 fseek(file, 0L, SEEK_END);
204 int64_t file_size = ftell(file);
205 fclose(file);
206
207 // We are deliberately using low level protobuf functions to get the stats
208 // since the convenience functions in the parser would CHECK that the events
209 // are well formed.
210 std::map<webrtc::rtclog::Event::EventType, Stats> stats;
211 int malformed_events = 0;
212 size_t malformed_event_size = 0;
213 size_t accumulated_event_size = 0;
214 for (const webrtc::rtclog::Event& event : events) {
215 size_t serialized_size = event.ByteSize();
216 // When the event is written on the disk, it is part of an EventStream
217 // object. The event stream will prepend a 1 byte field number/wire type,
218 // and a varint encoding (base 128) of the event length.
219 serialized_size =
220 1 + (1 + (serialized_size > 127) + (serialized_size > 16383)) +
221 serialized_size;
222
223 if (event.has_type() && event.has_timestamp_us()) {
224 stats[event.type()].count++;
225 stats[event.type()].total_size += serialized_size;
226 } else {
227 // The event is missing the type or the timestamp field.
228 malformed_events++;
229 malformed_event_size += serialized_size;
230 }
231 accumulated_event_size += serialized_size;
232 }
233
234 printf("Type \tCount\tTotal size\tAverage size\tPercent\n");
235 printf(
236 "-----------------------------------------------------------------------"
237 "\n");
238 for (const auto it : stats) {
239 printf("%-22s\t%5d\t%10zu\t%12.2lf\t%7.2lf\n",
240 EventTypeToString(it.first).c_str(), it.second.count,
241 it.second.total_size,
242 static_cast<double>(it.second.total_size) / it.second.count,
243 static_cast<double>(it.second.total_size) / file_size * 100);
244 }
245 if (malformed_events != 0) {
246 printf("%-22s\t%5d\t%10zu\t%12.2lf\t%7.2lf\n", "MALFORMED",
247 malformed_events, malformed_event_size,
248 static_cast<double>(malformed_event_size) / malformed_events,
249 static_cast<double>(malformed_event_size) / file_size * 100);
250 }
251 if (file_size - accumulated_event_size != 0) {
252 printf("WARNING: %" PRId64 " bytes not accounted for\n",
253 file_size - accumulated_event_size);
254 }
255
256 return 0;
257}