| // Copyright 2017 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #ifndef NB_SCRIPT_STACKTRACE_H_ |
| #define NB_SCRIPT_STACKTRACE_H_ |
| |
| #include "cobalt/script/mozjs/util/stack_trace_helpers.h" |
| |
| #include <algorithm> |
| #include <sstream> |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "base/stringprintf.h" |
| #include "cobalt/script/mozjs/util/exception_helpers.h" |
| #include "cobalt/script/stack_frame.h" |
| #include "nb/thread_local_object.h" |
| #include "starboard/memory.h" |
| #include "starboard/once.h" |
| #include "starboard/string.h" |
| #include "starboard/types.h" |
| #include "third_party/mozjs/js/src/jsapi.h" |
| |
| namespace cobalt { |
| namespace script { |
| namespace mozjs { |
| namespace util { |
| namespace { |
| |
| typedef nb::ThreadLocalObject<StackTraceGenerator> ThreadLocalJsStackTracer; |
| |
| SB_ONCE_INITIALIZE_FUNCTION(ThreadLocalJsStackTracer, |
| s_thread_local_js_stack_tracer_singelton); |
| |
| void ToStringAppend(const StackFrame& sf, std::string* out) { |
| base::SStringPrintf(out, "%s(%d,%d):%s", sf.source_url.c_str(), |
| sf.line_number, sf.column_number, |
| sf.function_name.c_str()); |
| } |
| |
| } // namespace. |
| |
| void SetThreadLocalJSContext(JSContext* context) { |
| GetThreadLocalStackTraceGenerator()->set_js_context(context); |
| } |
| |
| JSContext* GetThreadLocalJSContext() { |
| return GetThreadLocalStackTraceGenerator()->js_context(); |
| } |
| |
| StackTraceGenerator* GetThreadLocalStackTraceGenerator() { |
| return s_thread_local_js_stack_tracer_singelton()->GetOrCreate(); |
| } |
| |
| //////////////////////////////////// IMPL ///////////////////////////////////// |
| |
| StackTraceGenerator::StackTraceGenerator() : js_context_(NULL) {} |
| StackTraceGenerator::~StackTraceGenerator() {} |
| |
| bool StackTraceGenerator::Valid() { return js_context_ != NULL; } |
| |
| bool StackTraceGenerator::GenerateStackTrace( |
| int depth, nb::RewindableVector<StackFrame>* out) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| out->rewindAll(); |
| if (!Valid()) { |
| return false; |
| } |
| GetStackTrace(js_context_, depth, out); |
| return !out->empty(); |
| } |
| |
| bool StackTraceGenerator::GenerateStackTraceLines( |
| int depth, nb::RewindableVector<std::string>* out) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| out->rewindAll(); |
| nb::RewindableVector<StackFrame>& stack_frames = scratch_data_.stack_frames_; |
| if (!GenerateStackTrace(depth, &stack_frames)) { |
| return false; |
| } |
| |
| for (size_t i = 0; i < stack_frames.size(); ++i) { |
| std::string& current_string = out->grow(1); |
| current_string.assign(""); // Should not deallocate memory. |
| StackFrame& sf = stack_frames[i]; |
| ToStringAppend(sf, ¤t_string); |
| } |
| return true; |
| } |
| |
| bool StackTraceGenerator::GenerateStackTraceString(int depth, |
| std::string* out) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| out->assign(""); // Should not deallocate memory. |
| |
| nb::RewindableVector<StackFrame>& stack_frames = scratch_data_.stack_frames_; |
| if (!GenerateStackTrace(depth, &stack_frames)) { |
| return false; |
| } |
| |
| for (size_t i = 0; i < stack_frames.size(); ++i) { |
| cobalt::script::StackFrame& sf = stack_frames[i]; |
| ToStringAppend(sf, out); |
| if (i < stack_frames.size() - 1) { |
| base::SStringPrintf(out, "\n"); |
| } |
| } |
| return true; |
| } |
| |
| bool StackTraceGenerator::GenerateStackTraceString(int depth, char* buff, |
| size_t buff_size) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| SbMemorySet(buff, 0, buff_size); |
| std::string& scratch_symbol = scratch_data_.symbol_; |
| |
| if (!GenerateStackTraceString(depth, &scratch_symbol)) { |
| return false; |
| } |
| |
| SbStringCopy(buff, scratch_symbol.c_str(), buff_size); |
| return true; |
| } |
| |
| JSContext* StackTraceGenerator::js_context() { return js_context_; } |
| |
| void StackTraceGenerator::set_js_context(JSContext* js_ctx) { |
| js_context_ = js_ctx; |
| } |
| |
| } // namespace util |
| } // namespace mozjs |
| } // namespace script |
| } // namespace cobalt |
| |
| #endif // NB_SCRIPT_STACKTRACE_H_ |