зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
28b4f88f22
Коммит
3f4db61371
|
@ -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.
|
||||
|
|
Загрузка…
Ссылка в новой задаче