forked from mirrors/gecko-dev
This introduces the machinery needed to generate crash annotations from a YAML file. The relevant C++ functions are updated to take a typed enum. JavaScript calls are unaffected but they will throw if the string argument does not correspond to one of the known entries in the C++ enum. The existing whitelists and blacklists of annotations are also generated from the YAML file and all duplicate code related to them has been consolidated. Once written out to the .extra file the annotations are converted in string form and are no different than the existing ones. All existing annotations have been included in the list (and some obsolete ones have been removed) and all call sites have been updated including tests where appropriate. --HG-- extra : source : 4f6c43f2830701ec5552e08e3f1b06fe6d045860
418 lines
12 KiB
C++
418 lines
12 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/Move.h"
|
|
#if defined(ACCESSIBILITY)
|
|
#include "HandlerData.h"
|
|
#include "mozilla/a11y/Platform.h"
|
|
#include "mozilla/mscom/ActivationContext.h"
|
|
#endif // defined(ACCESSIBILITY)
|
|
#include "mozilla/mscom/EnsureMTA.h"
|
|
#include "mozilla/mscom/ProxyStream.h"
|
|
#include "mozilla/mscom/Utils.h"
|
|
#include "mozilla/ScopeExit.h"
|
|
|
|
#include "mozilla/mscom/Objref.h"
|
|
#include "nsExceptionHandler.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "RegistrationAnnotator.h"
|
|
|
|
#include <windows.h>
|
|
#include <objbase.h>
|
|
#include <shlwapi.h>
|
|
|
|
namespace mozilla {
|
|
namespace mscom {
|
|
|
|
ProxyStream::ProxyStream()
|
|
: mGlobalLockedBuf(nullptr)
|
|
, mHGlobal(nullptr)
|
|
, mBufSize(0)
|
|
, mPreserveStream(false)
|
|
{
|
|
}
|
|
|
|
// GetBuffer() fails with this variant, but that's okay because we're just
|
|
// reconstructing the stream from a buffer anyway.
|
|
ProxyStream::ProxyStream(REFIID aIID, const BYTE* aInitBuf,
|
|
const int aInitBufSize, Environment* aEnv)
|
|
: mGlobalLockedBuf(nullptr)
|
|
, mHGlobal(nullptr)
|
|
, mBufSize(aInitBufSize)
|
|
, mPreserveStream(false)
|
|
{
|
|
CrashReporter::Annotation kCrashReportKey =
|
|
CrashReporter::Annotation::ProxyStreamUnmarshalStatus;
|
|
|
|
if (!aInitBufSize) {
|
|
CrashReporter::AnnotateCrashReport(kCrashReportKey,
|
|
NS_LITERAL_CSTRING("!aInitBufSize"));
|
|
// We marshaled a nullptr. Nothing else to do here.
|
|
return;
|
|
}
|
|
|
|
HRESULT createStreamResult = CreateStream(aInitBuf, aInitBufSize,
|
|
getter_AddRefs(mStream));
|
|
if (FAILED(createStreamResult)) {
|
|
nsPrintfCString hrAsStr("0x%08X", createStreamResult);
|
|
CrashReporter::AnnotateCrashReport(kCrashReportKey, hrAsStr);
|
|
return;
|
|
}
|
|
|
|
// NB: We can't check for a null mStream until after we have checked for
|
|
// the zero aInitBufSize above. This is because InitStream will also fail
|
|
// in that case, even though marshaling a nullptr is allowable.
|
|
MOZ_ASSERT(mStream);
|
|
if (!mStream) {
|
|
CrashReporter::AnnotateCrashReport(kCrashReportKey,
|
|
NS_LITERAL_CSTRING("!mStream"));
|
|
return;
|
|
}
|
|
|
|
#if defined(ACCESSIBILITY)
|
|
const uint32_t expectedStreamLen = GetOBJREFSize(WrapNotNull(mStream));
|
|
nsAutoCString strActCtx;
|
|
nsAutoString manifestPath;
|
|
#endif // defined(ACCESSIBILITY)
|
|
|
|
HRESULT unmarshalResult = S_OK;
|
|
|
|
// We need to convert to an interface here otherwise we mess up const
|
|
// correctness with IPDL. We'll request an IUnknown and then QI the
|
|
// actual interface later.
|
|
|
|
#if defined(ACCESSIBILITY)
|
|
auto marshalFn = [this, &strActCtx, &manifestPath, &unmarshalResult, &aIID, aEnv]() -> void
|
|
#else
|
|
auto marshalFn = [this, &unmarshalResult, &aIID, aEnv]() -> void
|
|
#endif // defined(ACCESSIBILITY)
|
|
{
|
|
if (aEnv) {
|
|
bool pushOk = aEnv->Push();
|
|
MOZ_DIAGNOSTIC_ASSERT(pushOk);
|
|
if (!pushOk) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
auto popEnv = MakeScopeExit([aEnv]() -> void {
|
|
if (!aEnv) {
|
|
return;
|
|
}
|
|
|
|
bool popOk = aEnv->Pop();
|
|
MOZ_DIAGNOSTIC_ASSERT(popOk);
|
|
});
|
|
|
|
#if defined(ACCESSIBILITY)
|
|
auto curActCtx = ActivationContext::GetCurrent();
|
|
if (curActCtx.isOk()) {
|
|
strActCtx.AppendPrintf("0x%p", curActCtx.unwrap());
|
|
} else {
|
|
strActCtx.AppendPrintf("HRESULT 0x%08X", curActCtx.unwrapErr());
|
|
}
|
|
|
|
ActivationContext::GetCurrentManifestPath(manifestPath);
|
|
#endif // defined(ACCESSIBILITY)
|
|
|
|
unmarshalResult =
|
|
::CoUnmarshalInterface(mStream, aIID, getter_AddRefs(mUnmarshaledProxy));
|
|
MOZ_ASSERT(SUCCEEDED(unmarshalResult));
|
|
};
|
|
|
|
if (XRE_IsParentProcess()) {
|
|
// We'll marshal this stuff directly using the current thread, therefore its
|
|
// proxy will reside in the same apartment as the current thread.
|
|
marshalFn();
|
|
} else {
|
|
// When marshaling in child processes, we want to force the MTA.
|
|
EnsureMTA mta(marshalFn);
|
|
}
|
|
|
|
mStream = nullptr;
|
|
|
|
if (FAILED(unmarshalResult) || !mUnmarshaledProxy) {
|
|
nsPrintfCString hrAsStr("0x%08X", unmarshalResult);
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::CoUnmarshalInterfaceResult, hrAsStr);
|
|
AnnotateInterfaceRegistration(aIID);
|
|
if (!mUnmarshaledProxy) {
|
|
CrashReporter::AnnotateCrashReport(kCrashReportKey,
|
|
NS_LITERAL_CSTRING("!mUnmarshaledProxy"));
|
|
}
|
|
|
|
#if defined(ACCESSIBILITY)
|
|
AnnotateClassRegistration(CLSID_AccessibleHandler);
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::UnmarshalActCtx, strActCtx);
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::UnmarshalActCtxManifestPath,
|
|
NS_ConvertUTF16toUTF8(manifestPath));
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::A11yHandlerRegistered,
|
|
a11y::IsHandlerRegistered() ? NS_LITERAL_CSTRING("true")
|
|
: NS_LITERAL_CSTRING("false"));
|
|
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::ExpectedStreamLen, expectedStreamLen);
|
|
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::ActualStreamLen, aInitBufSize);
|
|
#endif // defined(ACCESSIBILITY)
|
|
}
|
|
}
|
|
|
|
ProxyStream::ProxyStream(ProxyStream&& aOther)
|
|
: mGlobalLockedBuf(nullptr)
|
|
, mHGlobal(nullptr)
|
|
, mBufSize(0)
|
|
, mPreserveStream(false)
|
|
{
|
|
*this = std::move(aOther);
|
|
}
|
|
|
|
ProxyStream&
|
|
ProxyStream::operator=(ProxyStream&& aOther)
|
|
{
|
|
if (mHGlobal && mGlobalLockedBuf) {
|
|
DebugOnly<BOOL> result = ::GlobalUnlock(mHGlobal);
|
|
MOZ_ASSERT(!result && ::GetLastError() == NO_ERROR);
|
|
}
|
|
|
|
mStream = std::move(aOther.mStream);
|
|
|
|
mGlobalLockedBuf = aOther.mGlobalLockedBuf;
|
|
aOther.mGlobalLockedBuf = nullptr;
|
|
|
|
// ::GlobalFree() was called implicitly when mStream was replaced.
|
|
mHGlobal = aOther.mHGlobal;
|
|
aOther.mHGlobal = nullptr;
|
|
|
|
mBufSize = aOther.mBufSize;
|
|
aOther.mBufSize = 0;
|
|
|
|
mUnmarshaledProxy = std::move(aOther.mUnmarshaledProxy);
|
|
|
|
mPreserveStream = aOther.mPreserveStream;
|
|
return *this;
|
|
}
|
|
|
|
ProxyStream::~ProxyStream()
|
|
{
|
|
if (mHGlobal && mGlobalLockedBuf) {
|
|
DebugOnly<BOOL> result = ::GlobalUnlock(mHGlobal);
|
|
MOZ_ASSERT(!result && ::GetLastError() == NO_ERROR);
|
|
// ::GlobalFree() is called implicitly when mStream is released
|
|
}
|
|
|
|
// If this assert triggers then we will be leaking a marshaled proxy!
|
|
// Call GetPreservedStream to obtain a preservable stream and then save it
|
|
// until the proxy is no longer needed.
|
|
MOZ_ASSERT(!mPreserveStream);
|
|
}
|
|
|
|
const BYTE*
|
|
ProxyStream::GetBuffer(int& aReturnedBufSize) const
|
|
{
|
|
aReturnedBufSize = 0;
|
|
if (!mStream) {
|
|
return nullptr;
|
|
}
|
|
if (!mGlobalLockedBuf) {
|
|
return nullptr;
|
|
}
|
|
aReturnedBufSize = mBufSize;
|
|
return mGlobalLockedBuf;
|
|
}
|
|
|
|
PreservedStreamPtr
|
|
ProxyStream::GetPreservedStream()
|
|
{
|
|
MOZ_ASSERT(mStream);
|
|
MOZ_ASSERT(mHGlobal);
|
|
|
|
if (!mStream || !mPreserveStream) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Clone the stream so that the result has a distinct seek pointer.
|
|
RefPtr<IStream> cloned;
|
|
HRESULT hr = mStream->Clone(getter_AddRefs(cloned));
|
|
if (FAILED(hr)) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Ensure the stream is rewound. We do this because CoReleaseMarshalData needs
|
|
// the stream to be pointing to the beginning of the marshal data.
|
|
LARGE_INTEGER pos;
|
|
pos.QuadPart = 0LL;
|
|
hr = cloned->Seek(pos, STREAM_SEEK_SET, nullptr);
|
|
if (FAILED(hr)) {
|
|
return nullptr;
|
|
}
|
|
|
|
mPreserveStream = false;
|
|
return ToPreservedStreamPtr(std::move(cloned));
|
|
}
|
|
|
|
bool
|
|
ProxyStream::GetInterface(void** aOutInterface)
|
|
{
|
|
// We should not have a locked buffer on this side
|
|
MOZ_ASSERT(!mGlobalLockedBuf);
|
|
MOZ_ASSERT(aOutInterface);
|
|
|
|
if (!aOutInterface) {
|
|
return false;
|
|
}
|
|
|
|
*aOutInterface = mUnmarshaledProxy.release();
|
|
return true;
|
|
}
|
|
|
|
ProxyStream::ProxyStream(REFIID aIID, IUnknown* aObject, Environment* aEnv,
|
|
ProxyStreamFlags aFlags)
|
|
: mGlobalLockedBuf(nullptr)
|
|
, mHGlobal(nullptr)
|
|
, mBufSize(0)
|
|
, mPreserveStream(aFlags & ProxyStreamFlags::ePreservable)
|
|
{
|
|
if (!aObject) {
|
|
return;
|
|
}
|
|
|
|
RefPtr<IStream> stream;
|
|
HGLOBAL hglobal = NULL;
|
|
int streamSize = 0;
|
|
DWORD mshlFlags = mPreserveStream ? MSHLFLAGS_TABLESTRONG : MSHLFLAGS_NORMAL;
|
|
|
|
HRESULT createStreamResult = S_OK;
|
|
HRESULT marshalResult = S_OK;
|
|
HRESULT statResult = S_OK;
|
|
HRESULT getHGlobalResult = S_OK;
|
|
|
|
nsAutoString manifestPath;
|
|
|
|
auto marshalFn = [this, &aIID, aObject, mshlFlags, &stream, &streamSize,
|
|
&hglobal, &createStreamResult, &marshalResult, &statResult,
|
|
&getHGlobalResult, aEnv, &manifestPath]() -> void
|
|
{
|
|
if (aEnv) {
|
|
bool pushOk = aEnv->Push();
|
|
MOZ_DIAGNOSTIC_ASSERT(pushOk);
|
|
if (!pushOk) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
auto popEnv = MakeScopeExit([aEnv]() -> void {
|
|
if (!aEnv) {
|
|
return;
|
|
}
|
|
|
|
bool popOk = aEnv->Pop();
|
|
MOZ_DIAGNOSTIC_ASSERT(popOk);
|
|
});
|
|
|
|
createStreamResult = ::CreateStreamOnHGlobal(nullptr, TRUE,
|
|
getter_AddRefs(stream));
|
|
if (FAILED(createStreamResult)) {
|
|
return;
|
|
}
|
|
|
|
#if defined(ACCESSIBILITY)
|
|
ActivationContext::GetCurrentManifestPath(manifestPath);
|
|
#endif // defined(ACCESSIBILITY)
|
|
|
|
marshalResult = ::CoMarshalInterface(stream, aIID, aObject, MSHCTX_LOCAL,
|
|
nullptr, mshlFlags);
|
|
MOZ_ASSERT(marshalResult != E_INVALIDARG);
|
|
if (FAILED(marshalResult)) {
|
|
return;
|
|
}
|
|
|
|
STATSTG statstg;
|
|
statResult = stream->Stat(&statstg, STATFLAG_NONAME);
|
|
if (SUCCEEDED(statResult)) {
|
|
streamSize = static_cast<int>(statstg.cbSize.LowPart);
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
getHGlobalResult = ::GetHGlobalFromStream(stream, &hglobal);
|
|
MOZ_ASSERT(SUCCEEDED(getHGlobalResult));
|
|
};
|
|
|
|
if (XRE_IsParentProcess()) {
|
|
// We'll marshal this stuff directly using the current thread, therefore its
|
|
// stub will reside in the same apartment as the current thread.
|
|
marshalFn();
|
|
} else {
|
|
// When marshaling in child processes, we want to force the MTA.
|
|
EnsureMTA mta(marshalFn);
|
|
}
|
|
|
|
if (FAILED(createStreamResult)) {
|
|
nsPrintfCString hrAsStr("0x%08X", createStreamResult);
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::CreateStreamOnHGlobalFailure, hrAsStr);
|
|
}
|
|
|
|
if (FAILED(marshalResult)) {
|
|
AnnotateInterfaceRegistration(aIID);
|
|
nsPrintfCString hrAsStr("0x%08X", marshalResult);
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::CoMarshalInterfaceFailure, hrAsStr);
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::MarshalActCtxManifestPath,
|
|
NS_ConvertUTF16toUTF8(manifestPath));
|
|
}
|
|
|
|
if (FAILED(statResult)) {
|
|
nsPrintfCString hrAsStr("0x%08X", statResult);
|
|
CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::StatFailure,
|
|
hrAsStr);
|
|
}
|
|
|
|
if (FAILED(getHGlobalResult)) {
|
|
nsPrintfCString hrAsStr("0x%08X", getHGlobalResult);
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::GetHGlobalFromStreamFailure, hrAsStr);
|
|
}
|
|
|
|
mStream = std::move(stream);
|
|
|
|
if (streamSize) {
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::ProxyStreamSizeFrom,
|
|
NS_LITERAL_CSTRING("IStream::Stat"));
|
|
mBufSize = streamSize;
|
|
}
|
|
|
|
if (!hglobal) {
|
|
return;
|
|
}
|
|
|
|
mGlobalLockedBuf = reinterpret_cast<BYTE*>(::GlobalLock(hglobal));
|
|
mHGlobal = hglobal;
|
|
|
|
// If we couldn't get the stream size directly from mStream, we may use
|
|
// the size of the memory block allocated by the HGLOBAL, though it might
|
|
// be larger than the actual stream size.
|
|
if (!streamSize) {
|
|
CrashReporter::AnnotateCrashReport(
|
|
CrashReporter::Annotation::ProxyStreamSizeFrom,
|
|
NS_LITERAL_CSTRING("GlobalSize"));
|
|
mBufSize = static_cast<int>(::GlobalSize(hglobal));
|
|
}
|
|
|
|
CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::ProxyStreamSize,
|
|
mBufSize);
|
|
}
|
|
|
|
} // namespace mscom
|
|
} // namespace mozilla
|
|
|