1/*
2 * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24
25#include "precompiled.hpp"
26#include "jfr/metadata/jfrSerializer.hpp"
27#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
28#include "jfr/recorder/repository/jfrChunkWriter.hpp"
29#include "jfr/recorder/service/jfrOptionSet.hpp"
30#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
31#include "jfr/utilities/jfrTypes.hpp"
32#include "memory/allocation.inline.hpp"
33#include "runtime/mutexLocker.hpp"
34#include "runtime/os.inline.hpp"
35#include "runtime/safepoint.hpp"
36#include "runtime/task.hpp"
37#include "runtime/vframe.inline.hpp"
38
39class vframeStreamSamples : public vframeStreamCommon {
40 public:
41 // constructor that starts with sender of frame fr (top_frame)
42 vframeStreamSamples(JavaThread *jt, frame fr, bool stop_at_java_call_stub);
43 void samples_next();
44 void stop() {}
45};
46
47vframeStreamSamples::vframeStreamSamples(JavaThread *jt, frame fr, bool stop_at_java_call_stub) : vframeStreamCommon(jt) {
48 _stop_at_java_call_stub = stop_at_java_call_stub;
49 _frame = fr;
50
51 // We must always have a valid frame to start filling
52 bool filled_in = fill_from_frame();
53 assert(filled_in, "invariant");
54}
55
56// Solaris SPARC Compiler1 needs an additional check on the grandparent
57// of the top_frame when the parent of the top_frame is interpreted and
58// the grandparent is compiled. However, in this method we do not know
59// the relationship of the current _frame relative to the top_frame so
60// we implement a more broad sanity check. When the previous callee is
61// interpreted and the current sender is compiled, we verify that the
62// current sender is also walkable. If it is not walkable, then we mark
63// the current vframeStream as at the end.
64void vframeStreamSamples::samples_next() {
65 // handle frames with inlining
66 if (_mode == compiled_mode &&
67 vframeStreamCommon::fill_in_compiled_inlined_sender()) {
68 return;
69 }
70
71 // handle general case
72 u4 loop_count = 0;
73 u4 loop_max = MAX_STACK_DEPTH * 2;
74 do {
75 loop_count++;
76 // By the time we get here we should never see unsafe but better safe then segv'd
77 if (loop_count > loop_max || !_frame.safe_for_sender(_thread)) {
78 _mode = at_end_mode;
79 return;
80 }
81 _frame = _frame.sender(&_reg_map);
82 } while (!fill_from_frame());
83}
84
85static JfrStackTraceRepository* _instance = NULL;
86
87JfrStackTraceRepository& JfrStackTraceRepository::instance() {
88 return *_instance;
89}
90
91JfrStackTraceRepository* JfrStackTraceRepository::create() {
92 assert(_instance == NULL, "invariant");
93 _instance = new JfrStackTraceRepository();
94 return _instance;
95}
96
97void JfrStackTraceRepository::destroy() {
98 assert(_instance != NULL, "invarinat");
99 delete _instance;
100 _instance = NULL;
101}
102
103JfrStackTraceRepository::JfrStackTraceRepository() : _next_id(0), _entries(0) {
104 memset(_table, 0, sizeof(_table));
105}
106class JfrFrameType : public JfrSerializer {
107 public:
108 void serialize(JfrCheckpointWriter& writer) {
109 writer.write_count(JfrStackFrame::NUM_FRAME_TYPES);
110 writer.write_key(JfrStackFrame::FRAME_INTERPRETER);
111 writer.write("Interpreted");
112 writer.write_key(JfrStackFrame::FRAME_JIT);
113 writer.write("JIT compiled");
114 writer.write_key(JfrStackFrame::FRAME_INLINE);
115 writer.write("Inlined");
116 writer.write_key(JfrStackFrame::FRAME_NATIVE);
117 writer.write("Native");
118 }
119};
120
121bool JfrStackTraceRepository::initialize() {
122 return JfrSerializer::register_serializer(TYPE_FRAMETYPE, false, true, new JfrFrameType());
123}
124
125size_t JfrStackTraceRepository::clear() {
126 MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
127 if (_entries == 0) {
128 return 0;
129 }
130 for (u4 i = 0; i < TABLE_SIZE; ++i) {
131 JfrStackTraceRepository::StackTrace* stacktrace = _table[i];
132 while (stacktrace != NULL) {
133 JfrStackTraceRepository::StackTrace* next = stacktrace->next();
134 delete stacktrace;
135 stacktrace = next;
136 }
137 }
138 memset(_table, 0, sizeof(_table));
139 const size_t processed = _entries;
140 _entries = 0;
141 return processed;
142}
143
144traceid JfrStackTraceRepository::add_trace(const JfrStackTrace& stacktrace) {
145 MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
146 const size_t index = stacktrace._hash % TABLE_SIZE;
147 const StackTrace* table_entry = _table[index];
148
149 while (table_entry != NULL) {
150 if (table_entry->equals(stacktrace)) {
151 return table_entry->id();
152 }
153 table_entry = table_entry->next();
154 }
155
156 if (!stacktrace.have_lineno()) {
157 return 0;
158 }
159
160 traceid id = ++_next_id;
161 _table[index] = new StackTrace(id, stacktrace, _table[index]);
162 ++_entries;
163 return id;
164}
165
166traceid JfrStackTraceRepository::add(const JfrStackTrace& stacktrace) {
167 traceid tid = instance().add_trace(stacktrace);
168 if (tid == 0) {
169 stacktrace.resolve_linenos();
170 tid = instance().add_trace(stacktrace);
171 }
172 assert(tid != 0, "invariant");
173 return tid;
174}
175
176traceid JfrStackTraceRepository::record(Thread* thread, int skip /* 0 */) {
177 assert(thread == Thread::current(), "invariant");
178 JfrThreadLocal* const tl = thread->jfr_thread_local();
179 assert(tl != NULL, "invariant");
180 if (tl->has_cached_stack_trace()) {
181 return tl->cached_stack_trace_id();
182 }
183 if (!thread->is_Java_thread() || thread->is_hidden_from_external_view()) {
184 return 0;
185 }
186 JfrStackFrame* frames = tl->stackframes();
187 if (frames == NULL) {
188 // pending oom
189 return 0;
190 }
191 assert(frames != NULL, "invariant");
192 assert(tl->stackframes() == frames, "invariant");
193 return instance().record_for((JavaThread*)thread, skip,frames, tl->stackdepth());
194}
195
196traceid JfrStackTraceRepository::record_for(JavaThread* thread, int skip, JfrStackFrame *frames, u4 max_frames) {
197 JfrStackTrace stacktrace(frames, max_frames);
198 return stacktrace.record_safe(thread, skip) ? add(stacktrace) : 0;
199}
200
201traceid JfrStackTraceRepository::add(const JfrStackTrace* stacktrace, JavaThread* thread) {
202 assert(stacktrace != NULL, "invariant");
203 assert(thread != NULL, "invariant");
204 assert(stacktrace->hash() != 0, "invariant");
205 return add(*stacktrace);
206}
207
208bool JfrStackTraceRepository::fill_stacktrace_for(JavaThread* thread, JfrStackTrace* stacktrace, int skip) {
209 assert(thread == Thread::current(), "invariant");
210 assert(stacktrace != NULL, "invariant");
211 JfrThreadLocal* const tl = thread->jfr_thread_local();
212 assert(tl != NULL, "invariant");
213 const unsigned int cached_stacktrace_hash = tl->cached_stack_trace_hash();
214 if (cached_stacktrace_hash != 0) {
215 stacktrace->set_hash(cached_stacktrace_hash);
216 return true;
217 }
218 return stacktrace->record_safe(thread, skip, true);
219}
220
221size_t JfrStackTraceRepository::write_impl(JfrChunkWriter& sw, bool clear) {
222 MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
223 assert(_entries > 0, "invariant");
224 int count = 0;
225 for (u4 i = 0; i < TABLE_SIZE; ++i) {
226 JfrStackTraceRepository::StackTrace* stacktrace = _table[i];
227 while (stacktrace != NULL) {
228 JfrStackTraceRepository::StackTrace* next = stacktrace->next();
229 if (stacktrace->should_write()) {
230 stacktrace->write(sw);
231 ++count;
232 }
233 if (clear) {
234 delete stacktrace;
235 }
236 stacktrace = next;
237 }
238 }
239 if (clear) {
240 memset(_table, 0, sizeof(_table));
241 _entries = 0;
242 }
243 return count;
244}
245
246size_t JfrStackTraceRepository::write(JfrChunkWriter& sw, bool clear) {
247 return _entries > 0 ? write_impl(sw, clear) : 0;
248}
249
250traceid JfrStackTraceRepository::write(JfrCheckpointWriter& writer, traceid id, unsigned int hash) {
251 assert(JfrStacktrace_lock->owned_by_self(), "invariant");
252 const StackTrace* const trace = resolve_entry(hash, id);
253 assert(trace != NULL, "invariant");
254 assert(trace->hash() == hash, "invariant");
255 assert(trace->id() == id, "invariant");
256 trace->write(writer);
257 return id;
258}
259
260JfrStackTraceRepository::StackTrace::StackTrace(traceid id, const JfrStackTrace& trace, JfrStackTraceRepository::StackTrace* next) :
261 _next(next),
262 _frames(NULL),
263 _id(id),
264 _nr_of_frames(trace._nr_of_frames),
265 _hash(trace._hash),
266 _reached_root(trace._reached_root),
267 _written(false) {
268 if (_nr_of_frames > 0) {
269 _frames = NEW_C_HEAP_ARRAY(JfrStackFrame, _nr_of_frames, mtTracing);
270 memcpy(_frames, trace._frames, _nr_of_frames * sizeof(JfrStackFrame));
271 }
272}
273
274JfrStackTraceRepository::StackTrace::~StackTrace() {
275 if (_frames != NULL) {
276 FREE_C_HEAP_ARRAY(JfrStackFrame, _frames);
277 }
278}
279
280bool JfrStackTraceRepository::StackTrace::equals(const JfrStackTrace& rhs) const {
281 if (_reached_root != rhs._reached_root || _nr_of_frames != rhs._nr_of_frames || _hash != rhs._hash) {
282 return false;
283 }
284 for (u4 i = 0; i < _nr_of_frames; ++i) {
285 if (!_frames[i].equals(rhs._frames[i])) {
286 return false;
287 }
288 }
289 return true;
290}
291
292template <typename Writer>
293static void write_stacktrace(Writer& w, traceid id, bool reached_root, u4 nr_of_frames, const JfrStackFrame* frames) {
294 w.write((u8)id);
295 w.write((u1)!reached_root);
296 w.write(nr_of_frames);
297 for (u4 i = 0; i < nr_of_frames; ++i) {
298 frames[i].write(w);
299 }
300}
301
302void JfrStackTraceRepository::StackTrace::write(JfrChunkWriter& sw) const {
303 assert(!_written, "invariant");
304 write_stacktrace(sw, _id, _reached_root, _nr_of_frames, _frames);
305 _written = true;
306}
307
308void JfrStackTraceRepository::StackTrace::write(JfrCheckpointWriter& cpw) const {
309 write_stacktrace(cpw, _id, _reached_root, _nr_of_frames, _frames);
310}
311
312// JfrStackFrame
313
314bool JfrStackFrame::equals(const JfrStackFrame& rhs) const {
315 return _methodid == rhs._methodid && _bci == rhs._bci && _type == rhs._type;
316}
317
318template <typename Writer>
319static void write_frame(Writer& w, traceid methodid, int line, int bci, u1 type) {
320 w.write((u8)methodid);
321 w.write((u4)line);
322 w.write((u4)bci);
323 w.write((u8)type);
324}
325
326void JfrStackFrame::write(JfrChunkWriter& cw) const {
327 write_frame(cw, _methodid, _line, _bci, _type);
328}
329
330void JfrStackFrame::write(JfrCheckpointWriter& cpw) const {
331 write_frame(cpw, _methodid, _line, _bci, _type);
332}
333
334// invariant is that the entry to be resolved actually exists in the table
335const JfrStackTraceRepository::StackTrace* JfrStackTraceRepository::resolve_entry(unsigned int hash, traceid id) const {
336 const size_t index = (hash % TABLE_SIZE);
337 const StackTrace* trace = _table[index];
338 while (trace != NULL && trace->id() != id) {
339 trace = trace->next();
340 }
341 assert(trace != NULL, "invariant");
342 assert(trace->hash() == hash, "invariant");
343 assert(trace->id() == id, "invariant");
344 return trace;
345}
346
347void JfrStackFrame::resolve_lineno() const {
348 assert(_method, "no method pointer");
349 assert(_line == 0, "already have linenumber");
350 _line = _method->line_number_from_bci(_bci);
351 _method = NULL;
352}
353
354void JfrStackTrace::set_frame(u4 frame_pos, JfrStackFrame& frame) {
355 assert(frame_pos < _max_frames, "illegal frame_pos");
356 _frames[frame_pos] = frame;
357}
358
359void JfrStackTrace::resolve_linenos() const {
360 for(unsigned int i = 0; i < _nr_of_frames; i++) {
361 _frames[i].resolve_lineno();
362 }
363 _lineno = true;
364}
365
366bool JfrStackTrace::record_safe(JavaThread* thread, int skip, bool leakp /* false */) {
367 assert(thread == Thread::current(), "Thread stack needs to be walkable");
368 vframeStream vfs(thread);
369 u4 count = 0;
370 _reached_root = true;
371 for(int i = 0; i < skip; i++) {
372 if (vfs.at_end()) {
373 break;
374 }
375 vfs.next();
376 }
377
378 while (!vfs.at_end()) {
379 if (count >= _max_frames) {
380 _reached_root = false;
381 break;
382 }
383 const Method* method = vfs.method();
384 const traceid mid = JfrTraceId::use(method, leakp);
385 int type = vfs.is_interpreted_frame() ? JfrStackFrame::FRAME_INTERPRETER : JfrStackFrame::FRAME_JIT;
386 int bci = 0;
387 if (method->is_native()) {
388 type = JfrStackFrame::FRAME_NATIVE;
389 } else {
390 bci = vfs.bci();
391 }
392 // Can we determine if it's inlined?
393 _hash = (_hash << 2) + (unsigned int)(((size_t)mid >> 2) + (bci << 4) + type);
394 _frames[count] = JfrStackFrame(mid, bci, type, method);
395 vfs.next();
396 count++;
397 }
398
399 _nr_of_frames = count;
400 return true;
401}
402
403bool JfrStackTrace::record_thread(JavaThread& thread, frame& frame) {
404 vframeStreamSamples st(&thread, frame, false);
405 u4 count = 0;
406 _reached_root = true;
407
408 while (!st.at_end()) {
409 if (count >= _max_frames) {
410 _reached_root = false;
411 break;
412 }
413 const Method* method = st.method();
414 if (!Method::is_valid_method(method)) {
415 // we throw away everything we've gathered in this sample since
416 // none of it is safe
417 return false;
418 }
419 const traceid mid = JfrTraceId::use(method);
420 int type = st.is_interpreted_frame() ? JfrStackFrame::FRAME_INTERPRETER : JfrStackFrame::FRAME_JIT;
421 int bci = 0;
422 if (method->is_native()) {
423 type = JfrStackFrame::FRAME_NATIVE;
424 } else {
425 bci = st.bci();
426 }
427 const int lineno = method->line_number_from_bci(bci);
428 // Can we determine if it's inlined?
429 _hash = (_hash << 2) + (unsigned int)(((size_t)mid >> 2) + (bci << 4) + type);
430 _frames[count] = JfrStackFrame(mid, bci, type, lineno);
431 st.samples_next();
432 count++;
433 }
434
435 _lineno = true;
436 _nr_of_frames = count;
437 return true;
438}
439
440void JfrStackTraceRepository::write_metadata(JfrCheckpointWriter& writer) {
441 JfrFrameType fct;
442 writer.write_type(TYPE_FRAMETYPE);
443 fct.serialize(writer);
444}
445