1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_JSON_JSON_TRACE_TOKENIZER_H_ 18 #define SRC_TRACE_PROCESSOR_IMPORTERS_JSON_JSON_TRACE_TOKENIZER_H_ 19 20 #include <cstdint> 21 #include <optional> 22 #include <string> 23 #include <vector> 24 25 #include "perfetto/base/status.h" 26 #include "perfetto/ext/base/string_view.h" 27 #include "src/trace_processor/importers/common/chunked_trace_reader.h" 28 #include "src/trace_processor/importers/systrace/systrace_line_tokenizer.h" 29 30 namespace Json { 31 class Value; 32 } 33 34 namespace perfetto::trace_processor { 35 36 class TraceProcessorContext; 37 38 // Visible for testing. 39 enum class ReadDictRes { 40 kFoundDict, 41 kNeedsMoreData, 42 kEndOfTrace, 43 kEndOfArray, 44 }; 45 46 // Parses at most one JSON dictionary and returns a pointer to the end of it, 47 // or nullptr if no dict could be detected. 48 // This is to avoid decoding the full trace in memory and reduce heap traffic. 49 // E.g. input: { a:1 b:{ c:2, d:{ e:3 } } } , { a:4, ... }, 50 // output: [ only this is parsed ] ^return value points here. 51 // Visible for testing. 52 ReadDictRes ReadOneJsonDict(const char* start, 53 const char* end, 54 base::StringView* value, 55 const char** next); 56 57 enum class ReadKeyRes { 58 kFoundKey, 59 kNeedsMoreData, 60 kEndOfDictionary, 61 kFatalError, 62 }; 63 64 // Parses at most one JSON key and returns a pointer to the start of the value 65 // associated with that key. 66 // This is to avoid decoding the full trace in memory and reduce heap traffic. 67 // E.g. input: a:1 b:{ c:2}} 68 // output: ^ return value points here, key is set to "a". 69 // Note: even if the whole key may be available, this method will return 70 // kNeedsMoreData until the first character of the value is available. 71 // Visible for testing. 72 ReadKeyRes ReadOneJsonKey(const char* start, 73 const char* end, 74 std::string* key, 75 const char** next); 76 77 // Takes as input a JSON dictionary and returns the value associated with 78 // the provided key (if it exists). 79 // Implementation note: this method does not currently support dictionaries 80 // which have arrays as JSON values because current users of this method 81 // do not require this. 82 // Visible for testing. 83 base::Status ExtractValueForJsonKey(base::StringView dict, 84 const std::string& key, 85 std::optional<std::string>* value); 86 87 enum class ReadSystemLineRes { 88 kFoundLine, 89 kNeedsMoreData, 90 kEndOfSystemTrace, 91 kFatalError, 92 }; 93 94 ReadSystemLineRes ReadOneSystemTraceLine(const char* start, 95 const char* end, 96 std::string* line, 97 const char** next); 98 99 // Reads a JSON trace in chunks and extracts top level json objects. 100 class JsonTraceTokenizer : public ChunkedTraceReader { 101 public: 102 explicit JsonTraceTokenizer(TraceProcessorContext*); 103 ~JsonTraceTokenizer() override; 104 105 // ChunkedTraceReader implementation. 106 base::Status Parse(TraceBlobView) override; 107 base::Status NotifyEndOfFile() override; 108 109 private: 110 // Enum which tracks which type of JSON trace we are dealing with. 111 enum class TraceFormat { 112 // Enum value when ther outer-most layer is a dictionary with multiple 113 // key value pairs 114 kOuterDictionary, 115 116 // Enum value when we only have trace events (i.e. the outermost 117 // layer is just a array of trace events). 118 kOnlyTraceEvents, 119 }; 120 121 // Enum which tracks our current position within the trace. 122 enum class TracePosition { 123 // This indicates that we are inside the outermost dictionary of the 124 // trace and need to read the next key of the dictionary. 125 // This position is only valid when the |format_| == |kOuterDictionary|. 126 kDictionaryKey, 127 128 // This indicates we are inside the systemTraceEvents string. 129 // This position is only valid when the |format_| == |kOuterDictionary|. 130 kInsideSystemTraceEventsString, 131 132 // This indicates where are inside the traceEvents array. 133 kInsideTraceEventsArray, 134 135 // This indicates we cannot parse any more data in the trace. 136 kEof, 137 }; 138 139 base::Status ParseInternal(const char* start, 140 const char* end, 141 const char** out); 142 143 base::Status ParseV8SampleEvent(base::StringView unparsed); 144 145 base::Status HandleTraceEvent(const char* start, 146 const char* end, 147 const char** out); 148 149 base::Status HandleDictionaryKey(const char* start, 150 const char* end, 151 const char** out); 152 153 base::Status HandleSystemTraceEvent(const char* start, 154 const char* end, 155 const char** out); 156 157 TraceProcessorContext* const context_; 158 159 TraceFormat format_ = TraceFormat::kOuterDictionary; 160 TracePosition position_ = TracePosition::kDictionaryKey; 161 162 SystraceLineTokenizer systrace_line_tokenizer_; 163 164 uint64_t offset_ = 0; 165 // Used to glue together JSON objects that span across two (or more) 166 // Parse boundaries. 167 std::vector<char> buffer_; 168 }; 169 170 } // namespace perfetto::trace_processor 171 172 #endif // SRC_TRACE_PROCESSOR_IMPORTERS_JSON_JSON_TRACE_TOKENIZER_H_ 173