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