xref: /spdk/lib/trace_parser/trace.cpp (revision 279b7babe484de7509a51d7cb74f6f45e6d3d5f9)
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