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