Bug 1789399 - Print out the creation stack and the last assignment stack of CheckedUnsafePtr when it is unsafe. r=dom-worker-reviewers,janv,smaug

Differential Revision: https://phabricator.services.mozilla.com/D162823
This commit is contained in:
Eden Chuang 2023-03-01 09:28:59 +00:00
Родитель 28b4f88f22
Коммит 3f4db61371
2 изменённых файлов: 183 добавлений и 5 удалений

Просмотреть файл

@ -10,12 +10,38 @@
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/DataMutex.h"
#include "mozilla/StackWalk.h"
#include "mozilla/StaticPrefs_dom.h"
#include "nsContentUtils.h"
#include "nsTArray.h"
#include "nsString.h"
#include <cstddef>
#include <type_traits>
#include <utility>
#if defined __has_builtin
# if __has_builtin(__builtin_FUNCTION)
# define bt_function __builtin_FUNCTION()
# else
# define bt_function "__builtin_FUNCTION() is undefined"
# endif
# if __has_builtin(__builtin_FILE)
# define bt_file __builtin_FILE()
# else
# define bt_file "__builtin_FILE() is undefined"
# endif
# if __has_builtin(__builtin_LINE)
# define bt_line __builtin_LINE()
# else
# define bt_line -1
# endif
#else
# define bt_function "__builtin_FUNCTION() is undefined"
# define bt_file "__builtin_FILE() is undefined"
# define bt_line -1
#endif
namespace mozilla {
enum class CheckingSupport {
Disabled,
@ -26,6 +52,95 @@ template <typename T>
class CheckedUnsafePtr;
namespace detail {
static constexpr auto kSourceFileRelativePathMap =
std::array<std::pair<nsLiteralCString, nsLiteralCString>, 1>{
{{"mozilla/dom/CheckedUnsafePtr.h"_ns,
"dom/quota/CheckedUnsafePtr.h"_ns}}};
static inline nsDependentCSubstring GetSourceFileRelativePath(
const nsACString& aSourceFilePath) {
static constexpr auto error = "ERROR"_ns;
static constexpr auto mozillaRelativeBase = "mozilla/"_ns;
static constexpr auto thisSourceFileRelativePath =
"/dom/quota/CheckedUnsafePtr.h"_ns;
static constexpr auto filePath = nsLiteralCString(__FILE__);
MOZ_ASSERT(StringEndsWith(filePath, thisSourceFileRelativePath));
static const auto sourceTreeBase = Substring(
filePath, 0, filePath.Length() - thisSourceFileRelativePath.Length());
if (MOZ_LIKELY(StringBeginsWith(aSourceFilePath, sourceTreeBase))) {
return Substring(aSourceFilePath, sourceTreeBase.Length() + 1);
}
// The source file could have been exported to the OBJDIR/dist/include
// directory, so we need to check that case as well.
static constexpr auto commonHSourceFileRelativePath =
"/mozilla/dom/quota/CheckedUnsafePtr.h"_ns;
MOZ_ASSERT(StringEndsWith(filePath, commonHSourceFileRelativePath));
static const auto objdirDistIncludeTreeBase = Substring(
filePath, 0, filePath.Length() - commonHSourceFileRelativePath.Length());
if (MOZ_LIKELY(
StringBeginsWith(aSourceFilePath, objdirDistIncludeTreeBase))) {
const auto sourceFileRelativePath =
Substring(aSourceFilePath, objdirDistIncludeTreeBase.Length() + 1);
// Exported source files don't have to use the same directory structure as
// original source files. Check if we have a mapping for the exported
// source file.
const auto foundIt = std::find_if(
kSourceFileRelativePathMap.cbegin(), kSourceFileRelativePathMap.cend(),
[&sourceFileRelativePath](const auto& entry) {
return entry.first == sourceFileRelativePath;
});
if (MOZ_UNLIKELY(foundIt != kSourceFileRelativePathMap.cend())) {
return Substring(foundIt->second, 0);
}
// If we don't have a mapping for it, just remove the mozilla/ prefix
// (if there's any).
if (MOZ_LIKELY(
StringBeginsWith(sourceFileRelativePath, mozillaRelativeBase))) {
return Substring(sourceFileRelativePath, mozillaRelativeBase.Length());
}
// At this point, we don't know how to transform the relative path of the
// exported source file back to the relative path of the original source
// file. This can happen when QM_TRY is used in an exported nsIFoo.h file.
// If you really need to use QM_TRY there, consider adding a new mapping
// for the exported source file.
return sourceFileRelativePath;
}
nsCString::const_iterator begin, end;
if (RFindInReadable("/"_ns, aSourceFilePath.BeginReading(begin),
aSourceFilePath.EndReading(end))) {
// Use the basename as a fallback, to avoid exposing any user parts of the
// path.
++begin;
return Substring(begin, aSourceFilePath.EndReading(end));
}
return nsDependentCSubstring{static_cast<mozilla::Span<const char>>(
static_cast<const nsCString&>(error))};
}
static inline void CheckedUnsafePtrStackCallback(uint32_t aFrameNumber,
void* aPC, void* aSP,
void* aClosure) {
auto* stack = static_cast<nsCString*>(aClosure);
MozCodeAddressDetails details;
MozDescribeCodeAddress(aPC, &details);
char buf[1025];
Unused << MozFormatCodeAddressDetails(buf, sizeof(buf), aFrameNumber, aPC,
&details);
stack->Append(buf);
stack->Append("\n");
}
class CheckedUnsafePtrBaseCheckingEnabled;
struct CheckedUnsafePtrCheckData {
@ -38,9 +153,12 @@ class CheckedUnsafePtrBaseCheckingEnabled {
friend class CheckedUnsafePtrBaseAccess;
protected:
constexpr CheckedUnsafePtrBaseCheckingEnabled() = default;
CheckedUnsafePtrBaseCheckingEnabled() = delete;
CheckedUnsafePtrBaseCheckingEnabled(
const CheckedUnsafePtrBaseCheckingEnabled& aOther) = default;
CheckedUnsafePtrBaseCheckingEnabled(const char* aFunction, const char* aFile,
const int aLine)
: mFunctionName(aFunction), mSourceFile(aFile), mLineNo(aLine) {}
// When copying an CheckedUnsafePtr, its mIsDangling member must be copied as
// well; otherwise the new copy might try to dereference a dangling pointer
@ -70,6 +188,21 @@ class CheckedUnsafePtrBaseCheckingEnabled {
}
}
void DumpDebugMsg() {
fprintf(stderr, "CheckedUnsafePtr [%p]\n", this);
fprintf(stderr, "Location of creation: %s, %s:%d\n", mFunctionName.get(),
GetSourceFileRelativePath(mSourceFile).BeginReading(), mLineNo);
fprintf(stderr, "Stack of creation:\n%s\n", mCreationStack.get());
fprintf(stderr, "Stack of last assignment\n%s\n\n",
mLastAssignmentStack.get());
}
nsCString mFunctionName{EmptyCString()};
nsCString mSourceFile{EmptyCString()};
int32_t mLineNo{-1};
nsCString mCreationStack{EmptyCString()};
nsCString mLastAssignmentStack{EmptyCString()};
private:
bool mIsDangling = false;
};
@ -78,6 +211,7 @@ class CheckedUnsafePtrBaseAccess {
protected:
static void SetDanglingFlag(CheckedUnsafePtrBaseCheckingEnabled& aBase) {
aBase.mIsDangling = true;
aBase.DumpDebugMsg();
}
};
@ -94,32 +228,70 @@ template <typename T>
class CheckedUnsafePtrBase<T, CheckingSupport::Enabled>
: detail::CheckedUnsafePtrBaseCheckingEnabled {
public:
MOZ_IMPLICIT constexpr CheckedUnsafePtrBase(const std::nullptr_t = nullptr)
: mRawPtr(nullptr) {}
MOZ_IMPLICIT constexpr CheckedUnsafePtrBase(
const std::nullptr_t = nullptr, const char* aFunction = bt_function,
const char* aFile = bt_file, const int32_t aLine = bt_line)
: detail::CheckedUnsafePtrBaseCheckingEnabled(aFunction, aFile, aLine),
mRawPtr(nullptr) {
if (StaticPrefs::dom_checkedUnsafePtr_dumpStacks_enabled()) {
MozStackWalk(CheckedUnsafePtrStackCallback, CallerPC(), 0,
&mCreationStack);
}
}
template <typename U, typename = EnableIfCompatible<T, U>>
MOZ_IMPLICIT CheckedUnsafePtrBase(const U& aPtr) {
MOZ_IMPLICIT CheckedUnsafePtrBase(const U& aPtr,
const char* aFunction = bt_function,
const char* aFile = bt_file,
const int32_t aLine = bt_line)
: detail::CheckedUnsafePtrBaseCheckingEnabled(aFunction, aFile, aLine) {
if (StaticPrefs::dom_checkedUnsafePtr_dumpStacks_enabled()) {
MozStackWalk(CheckedUnsafePtrStackCallback, CallerPC(), 0,
&mCreationStack);
}
Set(aPtr);
}
CheckedUnsafePtrBase(const CheckedUnsafePtrBase& aOther) {
CheckedUnsafePtrBase(const CheckedUnsafePtrBase& aOther,
const char* aFunction = bt_function,
const char* aFile = bt_file,
const int32_t aLine = bt_line)
: detail::CheckedUnsafePtrBaseCheckingEnabled(aFunction, aFile, aLine) {
if (StaticPrefs::dom_checkedUnsafePtr_dumpStacks_enabled()) {
MozStackWalk(CheckedUnsafePtrStackCallback, CallerPC(), 0,
&mCreationStack);
}
Set(aOther.Downcast());
}
~CheckedUnsafePtrBase() { Reset(); }
CheckedUnsafePtr<T>& operator=(const std::nullptr_t) {
// Assign to nullptr, no need to record the last assignment stack.
if (StaticPrefs::dom_checkedUnsafePtr_dumpStacks_enabled()) {
mLastAssignmentStack.Truncate();
}
Reset();
return Downcast();
}
template <typename U>
EnableIfCompatible<T, U, CheckedUnsafePtr<T>&> operator=(const U& aPtr) {
if (StaticPrefs::dom_checkedUnsafePtr_dumpStacks_enabled()) {
mLastAssignmentStack.Truncate();
MozStackWalk(CheckedUnsafePtrStackCallback, CallerPC(), 0,
&mLastAssignmentStack);
}
Replace(aPtr);
return Downcast();
}
CheckedUnsafePtrBase& operator=(const CheckedUnsafePtrBase& aOther) {
if (StaticPrefs::dom_checkedUnsafePtr_dumpStacks_enabled()) {
mLastAssignmentStack.Truncate();
MozStackWalk(CheckedUnsafePtrStackCallback, CallerPC(), 0,
&mLastAssignmentStack);
}
if (&aOther != this) {
Replace(aOther.Downcast());
}

Просмотреть файл

@ -3453,6 +3453,12 @@
mirror: never
#endif
# Use to control to dump CheckedUnsafePtr creation stack and assignment stacks.
- name: dom.checkedUnsafePtr.dumpStacks.enabled
type: RelaxedAtomicBool
value: false
mirror: always
# Determines within what distance of a tick mark, in pixels, dragging an input
# range range will snap the range's value to that tick mark. By default, this is
# half the default width of the range thumb.