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 private: 72 void populate_events(spdk_trace_history *history, int num_entries); 73 bool init(const spdk_trace_parser_opts *opts); 74 void cleanup(); 75 76 spdk_trace_histories *_histories; 77 size_t _map_size; 78 int _fd; 79 uint64_t _tsc_offset; 80 entry_map _entries; 81 }; 82 83 void 84 spdk_trace_parser::populate_events(spdk_trace_history *history, int num_entries) 85 { 86 int i, num_entries_filled; 87 spdk_trace_entry *e; 88 int first, last, lcore; 89 90 lcore = history->lcore; 91 e = history->entries; 92 93 num_entries_filled = num_entries; 94 while (e[num_entries_filled - 1].tsc == 0) { 95 num_entries_filled--; 96 } 97 98 if (num_entries == num_entries_filled) { 99 first = last = 0; 100 for (i = 1; i < num_entries; i++) { 101 if (e[i].tsc < e[first].tsc) { 102 first = i; 103 } 104 if (e[i].tsc > e[last].tsc) { 105 last = i; 106 } 107 } 108 } else { 109 first = 0; 110 last = num_entries_filled - 1; 111 } 112 113 /* 114 * We keep track of the highest first TSC out of all reactors. 115 * We will ignore any events that occured before this TSC on any 116 * other reactors. This will ensure we only print data for the 117 * subset of time where we have data across all reactors. 118 */ 119 if (e[first].tsc > _tsc_offset) { 120 _tsc_offset = e[first].tsc; 121 } 122 123 i = first; 124 while (1) { 125 if (e[i].tpoint_id != SPDK_TRACE_MAX_TPOINT_ID) { 126 _entries[entry_key(lcore, e[i].tsc)] = &e[i]; 127 } 128 if (i == last) { 129 break; 130 } 131 i++; 132 if (i == num_entries_filled) { 133 i = 0; 134 } 135 } 136 } 137 138 bool 139 spdk_trace_parser::init(const spdk_trace_parser_opts *opts) 140 { 141 spdk_trace_history *history; 142 struct stat st; 143 int rc, i; 144 145 switch (opts->mode) { 146 case SPDK_TRACE_PARSER_MODE_FILE: 147 _fd = open(opts->filename, O_RDONLY); 148 break; 149 case SPDK_TRACE_PARSER_MODE_SHM: 150 _fd = shm_open(opts->filename, O_RDONLY, 0600); 151 break; 152 default: 153 SPDK_ERRLOG("Invalid mode: %d\n", opts->mode); 154 return false; 155 } 156 157 if (_fd < 0) { 158 SPDK_ERRLOG("Could not open trace file: %s (%d)\n", opts->filename, errno); 159 return false; 160 } 161 162 rc = fstat(_fd, &st); 163 if (rc < 0) { 164 SPDK_ERRLOG("Could not get size of trace file: %s\n", opts->filename); 165 return false; 166 } 167 168 if ((size_t)st.st_size < sizeof(*_histories)) { 169 SPDK_ERRLOG("Invalid trace file: %s\n", opts->filename); 170 return false; 171 } 172 173 /* Map the header of trace file */ 174 _map_size = sizeof(*_histories); 175 _histories = static_cast<spdk_trace_histories *>(mmap(NULL, _map_size, PROT_READ, 176 MAP_SHARED, _fd, 0)); 177 if (_histories == MAP_FAILED) { 178 SPDK_ERRLOG("Could not mmap trace file: %s\n", opts->filename); 179 _histories = NULL; 180 return false; 181 } 182 183 /* Remap the entire trace file */ 184 _map_size = spdk_get_trace_histories_size(_histories); 185 munmap(_histories, sizeof(*_histories)); 186 if ((size_t)st.st_size < _map_size) { 187 SPDK_ERRLOG("Trace file %s is not valid\n", opts->filename); 188 _histories = NULL; 189 return false; 190 } 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 if (opts->lcore == SPDK_TRACE_MAX_LCORE) { 200 for (i = 0; i < SPDK_TRACE_MAX_LCORE; i++) { 201 history = spdk_get_per_lcore_history(_histories, i); 202 if (history->num_entries == 0 || history->entries[0].tsc == 0) { 203 continue; 204 } 205 206 populate_events(history, history->num_entries); 207 } 208 } else { 209 history = spdk_get_per_lcore_history(_histories, opts->lcore); 210 if (history->num_entries > 0 && history->entries[0].tsc != 0) { 211 populate_events(history, history->num_entries); 212 } 213 } 214 215 return true; 216 } 217 218 void 219 spdk_trace_parser::cleanup() 220 { 221 if (_histories != NULL) { 222 munmap(_histories, _map_size); 223 } 224 225 if (_fd > 0) { 226 close(_fd); 227 } 228 } 229 230 spdk_trace_parser::spdk_trace_parser(const spdk_trace_parser_opts *opts) : 231 _histories(NULL), 232 _map_size(0), 233 _fd(-1), 234 _tsc_offset(0) 235 { 236 if (!init(opts)) { 237 cleanup(); 238 throw std::exception(); 239 } 240 } 241 242 spdk_trace_parser::~spdk_trace_parser() 243 { 244 cleanup(); 245 } 246 247 struct spdk_trace_parser * 248 spdk_trace_parser_init(const struct spdk_trace_parser_opts *opts) 249 { 250 try { 251 return new spdk_trace_parser(opts); 252 } catch (...) { 253 return NULL; 254 } 255 } 256 257 void 258 spdk_trace_parser_cleanup(struct spdk_trace_parser *parser) 259 { 260 delete parser; 261 } 262 263 const struct spdk_trace_flags * 264 spdk_trace_parser_get_flags(const struct spdk_trace_parser *parser) 265 { 266 return parser->flags(); 267 } 268 269 uint64_t 270 spdk_trace_parser_get_tsc_offset(const struct spdk_trace_parser *parser) 271 { 272 return parser->tsc_offset(); 273 } 274