1 /*- 2 * BSD LICENSE 3 * 4 * Copyright (c) Intel Corporation. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * * Neither the name of Intel Corporation nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include "spdk/stdinc.h" 35 #include "spdk/log.h" 36 #include "spdk/trace_parser.h" 37 #include "spdk/util.h" 38 39 #include <exception> 40 #include <map> 41 #include <new> 42 43 struct entry_key { 44 entry_key(uint16_t _lcore, uint64_t _tsc) : lcore(_lcore), tsc(_tsc) {} 45 uint16_t lcore; 46 uint64_t tsc; 47 }; 48 49 class compare_entry_key 50 { 51 public: 52 bool operator()(const entry_key &first, const entry_key &second) const 53 { 54 if (first.tsc == second.tsc) { 55 return first.lcore < second.lcore; 56 } else { 57 return first.tsc < second.tsc; 58 } 59 } 60 }; 61 62 typedef std::map<entry_key, spdk_trace_entry *, compare_entry_key> entry_map; 63 64 struct spdk_trace_parser { 65 spdk_trace_parser(const spdk_trace_parser_opts *opts); 66 ~spdk_trace_parser(); 67 spdk_trace_parser(const spdk_trace_parser &) = delete; 68 spdk_trace_parser &operator=(const spdk_trace_parser &) = delete; 69 const spdk_trace_flags *flags() const { return &_histories->flags; } 70 uint64_t tsc_offset() const { return _tsc_offset; } 71 bool next_entry(spdk_trace_parser_entry *entry); 72 private: 73 void populate_events(spdk_trace_history *history, int num_entries); 74 bool init(const spdk_trace_parser_opts *opts); 75 void cleanup(); 76 77 spdk_trace_histories *_histories; 78 size_t _map_size; 79 int _fd; 80 uint64_t _tsc_offset; 81 entry_map _entries; 82 entry_map::iterator _iter; 83 }; 84 85 bool 86 spdk_trace_parser::next_entry(spdk_trace_parser_entry *entry) 87 { 88 if (_iter == _entries.end()) { 89 return false; 90 } 91 92 entry->entry = _iter->second; 93 entry->lcore = _iter->first.lcore; 94 95 _iter++; 96 return true; 97 } 98 99 void 100 spdk_trace_parser::populate_events(spdk_trace_history *history, int num_entries) 101 { 102 int i, num_entries_filled; 103 spdk_trace_entry *e; 104 int first, last, lcore; 105 106 lcore = history->lcore; 107 e = history->entries; 108 109 num_entries_filled = num_entries; 110 while (e[num_entries_filled - 1].tsc == 0) { 111 num_entries_filled--; 112 } 113 114 if (num_entries == num_entries_filled) { 115 first = last = 0; 116 for (i = 1; i < num_entries; i++) { 117 if (e[i].tsc < e[first].tsc) { 118 first = i; 119 } 120 if (e[i].tsc > e[last].tsc) { 121 last = i; 122 } 123 } 124 } else { 125 first = 0; 126 last = num_entries_filled - 1; 127 } 128 129 /* 130 * We keep track of the highest first TSC out of all reactors. 131 * We will ignore any events that occured before this TSC on any 132 * other reactors. This will ensure we only print data for the 133 * subset of time where we have data across all reactors. 134 */ 135 if (e[first].tsc > _tsc_offset) { 136 _tsc_offset = e[first].tsc; 137 } 138 139 i = first; 140 while (1) { 141 if (e[i].tpoint_id != SPDK_TRACE_MAX_TPOINT_ID) { 142 _entries[entry_key(lcore, e[i].tsc)] = &e[i]; 143 } 144 if (i == last) { 145 break; 146 } 147 i++; 148 if (i == num_entries_filled) { 149 i = 0; 150 } 151 } 152 } 153 154 bool 155 spdk_trace_parser::init(const spdk_trace_parser_opts *opts) 156 { 157 spdk_trace_history *history; 158 struct stat st; 159 int rc, i; 160 161 switch (opts->mode) { 162 case SPDK_TRACE_PARSER_MODE_FILE: 163 _fd = open(opts->filename, O_RDONLY); 164 break; 165 case SPDK_TRACE_PARSER_MODE_SHM: 166 _fd = shm_open(opts->filename, O_RDONLY, 0600); 167 break; 168 default: 169 SPDK_ERRLOG("Invalid mode: %d\n", opts->mode); 170 return false; 171 } 172 173 if (_fd < 0) { 174 SPDK_ERRLOG("Could not open trace file: %s (%d)\n", opts->filename, errno); 175 return false; 176 } 177 178 rc = fstat(_fd, &st); 179 if (rc < 0) { 180 SPDK_ERRLOG("Could not get size of trace file: %s\n", opts->filename); 181 return false; 182 } 183 184 if ((size_t)st.st_size < sizeof(*_histories)) { 185 SPDK_ERRLOG("Invalid trace file: %s\n", opts->filename); 186 return false; 187 } 188 189 /* Map the header of trace file */ 190 _map_size = sizeof(*_histories); 191 _histories = static_cast<spdk_trace_histories *>(mmap(NULL, _map_size, PROT_READ, 192 MAP_SHARED, _fd, 0)); 193 if (_histories == MAP_FAILED) { 194 SPDK_ERRLOG("Could not mmap trace file: %s\n", opts->filename); 195 _histories = NULL; 196 return false; 197 } 198 199 /* Remap the entire trace file */ 200 _map_size = spdk_get_trace_histories_size(_histories); 201 munmap(_histories, sizeof(*_histories)); 202 if ((size_t)st.st_size < _map_size) { 203 SPDK_ERRLOG("Trace file %s is not valid\n", opts->filename); 204 _histories = NULL; 205 return false; 206 } 207 _histories = static_cast<spdk_trace_histories *>(mmap(NULL, _map_size, PROT_READ, 208 MAP_SHARED, _fd, 0)); 209 if (_histories == MAP_FAILED) { 210 SPDK_ERRLOG("Could not mmap trace file: %s\n", opts->filename); 211 _histories = NULL; 212 return false; 213 } 214 215 if (opts->lcore == SPDK_TRACE_MAX_LCORE) { 216 for (i = 0; i < SPDK_TRACE_MAX_LCORE; i++) { 217 history = spdk_get_per_lcore_history(_histories, i); 218 if (history->num_entries == 0 || history->entries[0].tsc == 0) { 219 continue; 220 } 221 222 populate_events(history, history->num_entries); 223 } 224 } else { 225 history = spdk_get_per_lcore_history(_histories, opts->lcore); 226 if (history->num_entries > 0 && history->entries[0].tsc != 0) { 227 populate_events(history, history->num_entries); 228 } 229 } 230 231 _iter = _entries.begin(); 232 return true; 233 } 234 235 void 236 spdk_trace_parser::cleanup() 237 { 238 if (_histories != NULL) { 239 munmap(_histories, _map_size); 240 } 241 242 if (_fd > 0) { 243 close(_fd); 244 } 245 } 246 247 spdk_trace_parser::spdk_trace_parser(const spdk_trace_parser_opts *opts) : 248 _histories(NULL), 249 _map_size(0), 250 _fd(-1), 251 _tsc_offset(0) 252 { 253 if (!init(opts)) { 254 cleanup(); 255 throw std::exception(); 256 } 257 } 258 259 spdk_trace_parser::~spdk_trace_parser() 260 { 261 cleanup(); 262 } 263 264 struct spdk_trace_parser * 265 spdk_trace_parser_init(const struct spdk_trace_parser_opts *opts) 266 { 267 try { 268 return new spdk_trace_parser(opts); 269 } catch (...) { 270 return NULL; 271 } 272 } 273 274 void 275 spdk_trace_parser_cleanup(struct spdk_trace_parser *parser) 276 { 277 delete parser; 278 } 279 280 const struct spdk_trace_flags * 281 spdk_trace_parser_get_flags(const struct spdk_trace_parser *parser) 282 { 283 return parser->flags(); 284 } 285 286 uint64_t 287 spdk_trace_parser_get_tsc_offset(const struct spdk_trace_parser *parser) 288 { 289 return parser->tsc_offset(); 290 } 291 292 bool 293 spdk_trace_parser_next_entry(struct spdk_trace_parser *parser, 294 struct spdk_trace_parser_entry *entry) 295 { 296 return parser->next_entry(entry); 297 } 298