| // Copyright 2020 The SwiftShader Authors. 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. |
| |
| #include "ReactorDebugInfo.hpp" |
| #include "Print.hpp" |
| |
| #ifdef ENABLE_RR_DEBUG_INFO |
| |
| # include "boost/stacktrace.hpp" |
| |
| # include <algorithm> |
| # include <cctype> |
| # include <unordered_map> |
| |
| namespace rr { |
| |
| namespace { |
| std::string to_lower(std::string str) |
| { |
| std::transform(str.begin(), str.end(), str.begin(), |
| [](unsigned char c) { return std::tolower(c); }); |
| return str; |
| } |
| |
| bool endswith_lower(const std::string &str, const std::string &suffix) |
| { |
| size_t strLen = str.size(); |
| size_t suffixLen = suffix.size(); |
| |
| if(strLen < suffixLen) |
| { |
| return false; |
| } |
| |
| return to_lower(str).substr(strLen - suffixLen) == to_lower(suffix); |
| } |
| } // namespace |
| |
| Backtrace getCallerBacktrace(size_t limit /* = 0 */) |
| { |
| namespace bs = boost::stacktrace; |
| |
| auto shouldSkipFile = [](const std::string &fileSR) { |
| return fileSR.empty() || |
| endswith_lower(fileSR, "ReactorDebugInfo.cpp") || |
| endswith_lower(fileSR, "Reactor.cpp") || |
| endswith_lower(fileSR, "Reactor.hpp") || |
| endswith_lower(fileSR, "Traits.hpp") || |
| endswith_lower(fileSR, "stacktrace.hpp"); |
| }; |
| |
| auto offsetStackFrames = [](const bs::stacktrace &st) { |
| // Return a stack trace with every stack frame address offset by -1. We do this so that we get |
| // back the location of the function call, and not the location following it. We need this since |
| // all debug info emits are the result of a function call. Note that technically we shouldn't |
| // perform this offsetting on the top-most stack frame, but it doesn't matter as we discard it |
| // anyway (see shouldSkipFile). |
| |
| std::vector<bs::frame> result; |
| result.reserve(st.size()); |
| |
| for(bs::frame frame : st) |
| { |
| result.emplace_back(reinterpret_cast<void *>(reinterpret_cast<size_t>(frame.address()) - 1)); |
| } |
| |
| return result; |
| }; |
| |
| std::vector<Location> locations; |
| |
| // Cache to avoid expensive stacktrace lookups, especially since our use-case results in looking up the |
| // same call stack addresses many times. |
| static std::unordered_map<bs::frame::native_frame_ptr_t, Location> cache; |
| |
| for(bs::frame frame : offsetStackFrames(bs::stacktrace())) |
| { |
| Location location; |
| |
| auto iter = cache.find(frame.address()); |
| if(iter == cache.end()) |
| { |
| location.function.file = frame.source_file(); |
| location.function.name = frame.name(); |
| location.line = frame.source_line(); |
| cache[frame.address()] = location; |
| } |
| else |
| { |
| location = iter->second; |
| } |
| |
| if(shouldSkipFile(location.function.file)) |
| { |
| continue; |
| } |
| |
| locations.push_back(std::move(location)); |
| |
| if(limit > 0 && locations.size() >= limit) |
| { |
| break; |
| } |
| } |
| |
| std::reverse(locations.begin(), locations.end()); |
| |
| return locations; |
| } |
| |
| void emitPrintLocation(const Backtrace &backtrace) |
| { |
| # ifdef ENABLE_RR_EMIT_PRINT_LOCATION |
| static Location lastLocation; |
| if(backtrace.size() == 0) |
| { |
| return; |
| } |
| Location currLocation = backtrace[backtrace.size() - 1]; |
| if(currLocation != lastLocation) |
| { |
| rr::Print("rr> {0} [{1}:{2}]\n", currLocation.function.name.c_str(), currLocation.function.file.c_str(), currLocation.line); |
| lastLocation = std::move(currLocation); |
| } |
| # endif |
| } |
| |
| } // namespace rr |
| |
| #endif // ENABLE_RR_DEBUG_INFO |