Bug 1607047 - Include build ID in recordings and introduction message, r=jlast.

Differential Revision: https://phabricator.services.mozilla.com/D58697

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Brian Hackett 2020-01-05 01:29:34 +00:00
parent 0e24025af0
commit 30f811339f
5 changed files with 98 additions and 8 deletions

View File

@ -11,6 +11,7 @@
#include "mozilla/Sprintf.h"
#include "ProcessRewind.h"
#include "SpinLock.h"
#include "nsAppRunner.h"
#include <algorithm>
@ -331,15 +332,43 @@ void Stream::DumpEvents() {
// Recording
///////////////////////////////////////////////////////////////////////////////
// We expect to find this at the start of every recording.
static const uint64_t MagicValue = 0xd3e7f5fae445b3ac;
void GetCurrentBuildId(BuildId* aBuildId) {
int n = snprintf(aBuildId->mContents, sizeof(aBuildId->mContents), "macOS-%s",
PlatformBuildID());
MOZ_RELEASE_ASSERT((size_t)n + 1 <= sizeof(aBuildId->mContents));
}
struct Header {
uint64_t mMagic;
BuildId mBuildId;
};
Recording::Recording() : mMode(IsRecording() ? WRITE : READ) {
PodZero(&mLock);
PodZero(&mStreamLock);
if (IsReplaying()) {
if (IsRecording()) {
Header header;
header.mMagic = MagicValue;
GetCurrentBuildId(&header.mBuildId);
mContents.append((const uint8_t*)&header, sizeof(Header));
} else {
gDumpEvents = TestEnv("MOZ_REPLAYING_DUMP_EVENTS");
}
}
/* static */
void Recording::ExtractBuildId(const char* aContents, size_t aLength,
BuildId* aBuildId) {
MOZ_RELEASE_ASSERT(aLength >= sizeof(Header));
const Header* header = (const Header*)aContents;
MOZ_RELEASE_ASSERT(header->mMagic == MagicValue);
*aBuildId = header->mBuildId;
}
// The recording format is a series of chunks. Each chunk is a ChunkDescriptor
// followed by the compressed contents of the chunk itself.
struct ChunkDescriptor {
@ -361,9 +390,22 @@ void Recording::NewContents(const uint8_t* aContents, size_t aSize,
MOZ_RELEASE_ASSERT(Thread::CurrentIsMainThread());
MOZ_RELEASE_ASSERT(IsReading());
// Make sure the header matches when reading the first data in the recording.
size_t offset = 0;
if (mContents.empty()) {
MOZ_RELEASE_ASSERT(aSize >= sizeof(Header));
offset += sizeof(Header);
Header* header = (Header*) aContents;
MOZ_RELEASE_ASSERT(header->mMagic == MagicValue);
BuildId currentBuildId;
GetCurrentBuildId(&currentBuildId);
MOZ_RELEASE_ASSERT(currentBuildId.Matches(header->mBuildId));
}
mContents.append(aContents, aSize);
size_t offset = 0;
while (offset < aSize) {
MOZ_RELEASE_ASSERT(offset + sizeof(ChunkDescriptor) <= aSize);
ChunkDescriptor* desc = (ChunkDescriptor*)(aContents + offset);

View File

@ -207,6 +207,19 @@ class Stream {
bool ReadMismatchedEventData(ThreadEvent aEvent);
};
// All information about the platform and build where a recording was made.
struct BuildId {
char mContents[128];
BuildId() { PodZero(this); }
bool Matches(const BuildId& aOther) {
return !memcmp(this, &aOther, sizeof(*this));
}
};
// Get the build ID for the currently running process.
void GetCurrentBuildId(BuildId* aBuildId);
class Recording {
public:
enum Mode { WRITE, READ };
@ -259,6 +272,10 @@ class Recording {
// Flush all streams to the recording.
void Flush();
// Get the build ID embedded in a recording.
static void ExtractBuildId(const char* aContents, size_t aLength,
BuildId* aBuildId);
private:
StreamChunkLocation WriteChunk(StreamName aName, size_t aNameIndex,
const char* aStart, size_t aCompressedSize,

View File

@ -42,11 +42,17 @@ namespace recordreplay {
// below.
#define ForEachMessageType(_Macro) \
/* Messages sent from the middleman to the child process. */ \
/* Messages which can be interpreted or constructed by the cloud server. */ \
/* Avoid changing the message IDs for these. */ \
\
/* Sent at startup. */ \
/* Sent by the middleman at startup. */ \
_Macro(Introduction) \
\
/* An error occurred in the cloud server. */ \
_Macro(CloudError) \
\
/* Messages sent from the middleman to the child process. */ \
\
/* Sent to recording processes to indicate that the middleman will be running */ \
/* developer tools server-side code instead of the recording process itself. */ \
_Macro(SetDebuggerRunsInMiddleman) \
@ -82,6 +88,7 @@ namespace recordreplay {
/* Respond to a ping message */ \
_Macro(PingResponse) \
\
/* An unhandled recording divergence occurred and execution cannot continue. */ \
_Macro(UnhandledDivergence) \
\
/* A critical error occurred and execution cannot continue. The child will */ \
@ -187,7 +194,12 @@ struct Message {
};
struct IntroductionMessage : public Message {
// Used when replaying to describe the build that must be used for the replay.
BuildId mBuildId;
// Used when recording to specify the parent process pid.
base::ProcessId mParentPid;
uint32_t mArgc;
IntroductionMessage(uint32_t aSize, base::ProcessId aParentPid,
@ -263,13 +275,17 @@ struct JSONMessage : public Message {
typedef JSONMessage<MessageType::ManifestStart> ManifestStartMessage;
typedef JSONMessage<MessageType::ManifestFinished> ManifestFinishedMessage;
struct FatalErrorMessage : public Message {
explicit FatalErrorMessage(uint32_t aSize, uint32_t aForkId)
: Message(MessageType::FatalError, aSize, aForkId) {}
template <MessageType Type>
struct ErrorMessage : public Message {
explicit ErrorMessage(uint32_t aSize, uint32_t aForkId)
: Message(Type, aSize, aForkId) {}
const char* Error() const { return Data<FatalErrorMessage, const char>(); }
const char* Error() const { return Data<ErrorMessage<Type>, const char>(); }
};
typedef ErrorMessage<MessageType::FatalError> FatalErrorMessage;
typedef ErrorMessage<MessageType::CloudError> CloudErrorMessage;
typedef EmptyMessage<MessageType::UnhandledDivergence> UnhandledDivergenceMessage;
// The format for graphics data which will be sent to the middleman process.

View File

@ -51,6 +51,12 @@ void ChildProcessInfo::OnIncomingMessage(const Message& aMsg) {
OnCrash(nmsg.mForkId, nmsg.Error());
return;
}
case MessageType::CloudError: {
const auto& nmsg = static_cast<const CloudErrorMessage&>(aMsg);
Print("Fatal Cloud Error: %s\n", nmsg.Error());
MOZ_CRASH("Cloud Error");
return;
}
case MessageType::Paint:
UpdateGraphicsAfterPaint(static_cast<const PaintMessage&>(aMsg));
break;

View File

@ -343,6 +343,8 @@ void InitializeMiddleman(int aArgc, char* aArgv[], base::ProcessId aParentPid,
// Construct the message that will be sent to each child when starting up.
IntroductionMessage* msg = IntroductionMessage::New(aParentPid, aArgc, aArgv);
GetCurrentBuildId(&msg->mBuildId);
ChildProcessInfo::SetIntroductionMessage(msg);
MOZ_RELEASE_ASSERT(gProcessKind == ProcessKind::MiddlemanRecording ||
@ -379,6 +381,13 @@ void InitializeMiddleman(int aArgc, char* aArgv[], base::ProcessId aParentPid,
}
DirectCloseFile(fd);
// Update the build ID in the introduction message according to what we
// find in the recording. The introduction message is sent first to each
// replaying process, and when replaying in the cloud its contents will be
// analyzed to determine what binaries to use for the replay.
Recording::ExtractBuildId(gRecordingContents.begin(),
gRecordingContents.length(), &msg->mBuildId);
}
}