2018-07-22 14:49:17 +03:00
|
|
|
/* -*- 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 "ParentInternal.h"
|
|
|
|
|
|
|
|
#include "base/task.h"
|
|
|
|
#include "mozilla/dom/ContentChild.h"
|
|
|
|
#include "Thread.h"
|
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace recordreplay {
|
|
|
|
namespace parent {
|
|
|
|
|
|
|
|
// A saved introduction message for sending to all children.
|
|
|
|
static IntroductionMessage* gIntroductionMessage;
|
|
|
|
|
2019-02-26 01:12:51 +03:00
|
|
|
/* static */
|
|
|
|
void ChildProcessInfo::SetIntroductionMessage(IntroductionMessage* aMessage) {
|
2018-07-22 14:49:17 +03:00
|
|
|
gIntroductionMessage = aMessage;
|
|
|
|
}
|
|
|
|
|
2018-07-24 18:48:10 +03:00
|
|
|
ChildProcessInfo::ChildProcessInfo(
|
2020-01-03 23:43:08 +03:00
|
|
|
size_t aId, const Maybe<RecordingProcessData>& aRecordingProcessData)
|
2019-08-04 19:55:00 +03:00
|
|
|
: mRecording(aRecordingProcessData.isSome()) {
|
2018-07-22 14:49:17 +03:00
|
|
|
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
|
|
|
|
2020-01-03 23:43:08 +03:00
|
|
|
Channel::Kind kind =
|
|
|
|
IsRecording() ? Channel::Kind::MiddlemanRecord : Channel::Kind::MiddlemanReplay;
|
|
|
|
mChannel = new Channel(aId, kind, [=](Message::UniquePtr aMsg) {
|
|
|
|
ReceiveChildMessageOnMainThread(aId, std::move(aMsg));
|
|
|
|
});
|
2018-07-22 14:49:17 +03:00
|
|
|
|
2020-01-03 23:43:08 +03:00
|
|
|
LaunchSubprocess(aId, aRecordingProcessData);
|
2018-07-22 14:49:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
ChildProcessInfo::~ChildProcessInfo() {
|
|
|
|
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
2020-01-03 23:43:08 +03:00
|
|
|
SendMessage(TerminateMessage(0));
|
2018-07-22 14:49:17 +03:00
|
|
|
}
|
|
|
|
|
2019-05-13 02:16:36 +03:00
|
|
|
void ChildProcessInfo::OnIncomingMessage(const Message& aMsg) {
|
2018-07-22 14:49:17 +03:00
|
|
|
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
|
|
|
|
|
|
|
switch (aMsg.mType) {
|
2018-12-28 02:32:36 +03:00
|
|
|
case MessageType::FatalError: {
|
|
|
|
const FatalErrorMessage& nmsg =
|
|
|
|
static_cast<const FatalErrorMessage&>(aMsg);
|
2020-01-03 23:43:08 +03:00
|
|
|
OnCrash(nmsg.mForkId, nmsg.Error());
|
2018-12-28 02:32:36 +03:00
|
|
|
return;
|
|
|
|
}
|
2020-01-05 04:29:34 +03:00
|
|
|
case MessageType::CloudError: {
|
|
|
|
const auto& nmsg = static_cast<const CloudErrorMessage&>(aMsg);
|
|
|
|
Print("Fatal Cloud Error: %s\n", nmsg.Error());
|
|
|
|
MOZ_CRASH("Cloud Error");
|
|
|
|
return;
|
|
|
|
}
|
2018-12-28 02:32:36 +03:00
|
|
|
case MessageType::Paint:
|
2019-06-12 20:45:35 +03:00
|
|
|
UpdateGraphicsAfterPaint(static_cast<const PaintMessage&>(aMsg));
|
2018-12-28 02:32:36 +03:00
|
|
|
break;
|
2020-01-03 23:43:08 +03:00
|
|
|
case MessageType::PaintEncoded: {
|
|
|
|
const PaintEncodedMessage& nmsg =
|
|
|
|
static_cast<const PaintEncodedMessage&>(aMsg);
|
|
|
|
nsDependentCSubstring data(nmsg.BinaryData(), nmsg.BinaryDataSize());
|
|
|
|
nsAutoCString dataBinary;
|
|
|
|
if (NS_FAILED(Base64Decode(data, dataBinary))) {
|
|
|
|
MOZ_CRASH("Base64Decode failed");
|
|
|
|
}
|
|
|
|
UpdateGraphicsAfterRepaint(dataBinary);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MessageType::ManifestFinished: {
|
|
|
|
const auto& nmsg = static_cast<const ManifestFinishedMessage&>(aMsg);
|
|
|
|
js::ForwardManifestFinished(this, nmsg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MessageType::UnhandledDivergence: {
|
|
|
|
const auto& nmsg = static_cast<const UnhandledDivergenceMessage&>(aMsg);
|
|
|
|
js::ForwardUnhandledDivergence(this, nmsg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MessageType::PingResponse: {
|
|
|
|
const auto& nmsg = static_cast<const PingResponseMessage&>(aMsg);
|
|
|
|
js::ForwardPingResponse(this, nmsg);
|
2018-07-22 14:49:17 +03:00
|
|
|
break;
|
2020-01-03 23:43:08 +03:00
|
|
|
}
|
|
|
|
case MessageType::ExternalCallRequest: {
|
|
|
|
const auto& nmsg = static_cast<const ExternalCallRequestMessage&>(aMsg);
|
2018-12-29 21:22:13 +03:00
|
|
|
InfallibleVector<char> outputData;
|
2020-01-03 23:43:08 +03:00
|
|
|
ProcessExternalCall(nmsg.BinaryData(), nmsg.BinaryDataSize(),
|
|
|
|
&outputData);
|
|
|
|
Message::UniquePtr response(ExternalCallResponseMessage::New(
|
|
|
|
nmsg.mForkId, nmsg.mTag, outputData.begin(), outputData.length()));
|
2019-04-27 18:26:44 +03:00
|
|
|
SendMessage(std::move(*response));
|
2018-07-22 14:49:17 +03:00
|
|
|
break;
|
|
|
|
}
|
2020-01-03 23:43:08 +03:00
|
|
|
case MessageType::RecordingData: {
|
|
|
|
const auto& msg = static_cast<const RecordingDataMessage&>(aMsg);
|
|
|
|
MOZ_RELEASE_ASSERT(msg.mTag == gRecordingContents.length());
|
|
|
|
gRecordingContents.append(msg.BinaryData(), msg.BinaryDataSize());
|
2019-11-02 01:50:37 +03:00
|
|
|
break;
|
2020-01-03 23:43:08 +03:00
|
|
|
}
|
2018-12-28 02:32:36 +03:00
|
|
|
default:
|
|
|
|
break;
|
2018-07-22 14:49:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-27 18:26:44 +03:00
|
|
|
void ChildProcessInfo::SendMessage(Message&& aMsg) {
|
2018-07-22 14:49:17 +03:00
|
|
|
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
2019-04-27 18:26:44 +03:00
|
|
|
mChannel->SendMessage(std::move(aMsg));
|
2018-07-22 14:49:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Subprocess Management
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2018-07-22 14:55:36 +03:00
|
|
|
ipc::GeckoChildProcessHost* gRecordingProcess;
|
|
|
|
|
|
|
|
void GetArgumentsForChildProcess(base::ProcessId aMiddlemanPid,
|
|
|
|
uint32_t aChannelId,
|
|
|
|
const char* aRecordingFile, bool aRecording,
|
|
|
|
std::vector<std::string>& aExtraArgs) {
|
|
|
|
MOZ_RELEASE_ASSERT(IsMiddleman() || XRE_IsParentProcess());
|
|
|
|
|
|
|
|
aExtraArgs.push_back(gMiddlemanPidOption);
|
|
|
|
aExtraArgs.push_back(nsPrintfCString("%d", aMiddlemanPid).get());
|
|
|
|
|
|
|
|
aExtraArgs.push_back(gChannelIDOption);
|
|
|
|
aExtraArgs.push_back(nsPrintfCString("%d", (int)aChannelId).get());
|
|
|
|
|
|
|
|
aExtraArgs.push_back(gProcessKindOption);
|
|
|
|
aExtraArgs.push_back(nsPrintfCString("%d", aRecording
|
|
|
|
? (int)ProcessKind::Recording
|
|
|
|
: (int)ProcessKind::Replaying)
|
|
|
|
.get());
|
|
|
|
|
|
|
|
aExtraArgs.push_back(gRecordingFileOption);
|
|
|
|
aExtraArgs.push_back(aRecordingFile);
|
|
|
|
}
|
|
|
|
|
2018-07-24 18:48:10 +03:00
|
|
|
void ChildProcessInfo::LaunchSubprocess(
|
2020-01-03 23:43:08 +03:00
|
|
|
size_t aChannelId,
|
2018-07-24 18:48:10 +03:00
|
|
|
const Maybe<RecordingProcessData>& aRecordingProcessData) {
|
2020-01-03 19:43:40 +03:00
|
|
|
MOZ_RELEASE_ASSERT(IsRecording() == aRecordingProcessData.isSome());
|
2020-01-03 23:43:08 +03:00
|
|
|
|
|
|
|
MOZ_RELEASE_ASSERT(gIntroductionMessage);
|
|
|
|
SendMessage(std::move(*gIntroductionMessage));
|
|
|
|
|
2018-07-22 14:55:36 +03:00
|
|
|
if (IsRecording()) {
|
|
|
|
std::vector<std::string> extraArgs;
|
2020-01-03 23:43:08 +03:00
|
|
|
GetArgumentsForChildProcess(base::GetCurrentProcId(), aChannelId,
|
2018-07-22 14:55:36 +03:00
|
|
|
gRecordingFilename, /* aRecording = */ true,
|
|
|
|
extraArgs);
|
|
|
|
|
|
|
|
MOZ_RELEASE_ASSERT(!gRecordingProcess);
|
|
|
|
gRecordingProcess =
|
|
|
|
new ipc::GeckoChildProcessHost(GeckoProcessType_Content);
|
2018-07-24 18:46:38 +03:00
|
|
|
|
2018-07-24 18:48:10 +03:00
|
|
|
// Preferences data is conveyed to the recording process via fixed file
|
|
|
|
// descriptors on macOS.
|
|
|
|
gRecordingProcess->AddFdToRemap(aRecordingProcessData.ref().mPrefsHandle.fd,
|
|
|
|
kPrefsFileDescriptor);
|
|
|
|
ipc::FileDescriptor::UniquePlatformHandle prefMapHandle =
|
|
|
|
aRecordingProcessData.ref().mPrefMapHandle.ClonePlatformHandle();
|
|
|
|
gRecordingProcess->AddFdToRemap(prefMapHandle.get(),
|
|
|
|
kPrefMapFileDescriptor);
|
2018-07-24 18:46:38 +03:00
|
|
|
|
2018-07-22 14:55:36 +03:00
|
|
|
if (!gRecordingProcess->LaunchAndWaitForProcessHandle(extraArgs)) {
|
|
|
|
MOZ_CRASH("ChildProcessInfo::LaunchSubprocess");
|
|
|
|
}
|
2020-01-03 23:43:08 +03:00
|
|
|
|
|
|
|
SendGraphicsMemoryToChild();
|
2018-07-22 14:55:36 +03:00
|
|
|
} else {
|
2020-01-03 23:43:08 +03:00
|
|
|
UniquePtr<Message> msg(RecordingDataMessage::New(
|
|
|
|
0, 0, gRecordingContents.begin(), gRecordingContents.length()));
|
|
|
|
SendMessage(std::move(*msg));
|
|
|
|
dom::ContentChild::GetSingleton()->SendCreateReplayingProcess(aChannelId);
|
2018-07-22 14:49:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-03 23:43:08 +03:00
|
|
|
void ChildProcessInfo::OnCrash(size_t aForkId, const char* aWhy) {
|
2018-07-22 14:49:17 +03:00
|
|
|
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
|
|
|
|
2018-11-03 03:30:39 +03:00
|
|
|
// If a child process crashes or hangs then annotate the crash report.
|
2018-10-10 03:24:14 +03:00
|
|
|
CrashReporter::AnnotateCrashReport(
|
|
|
|
CrashReporter::Annotation::RecordReplayError, nsAutoCString(aWhy));
|
2018-11-03 03:30:39 +03:00
|
|
|
|
2019-08-04 19:55:00 +03:00
|
|
|
if (!IsRecording()) {
|
2020-01-09 21:03:59 +03:00
|
|
|
if (!UseCloudForReplayingProcesses()) {
|
|
|
|
// Notify the parent when a replaying process crashes so that a report can
|
|
|
|
// be generated.
|
|
|
|
dom::ContentChild::GetSingleton()->SendGenerateReplayCrashReport(GetId());
|
|
|
|
}
|
2019-08-04 19:55:00 +03:00
|
|
|
|
|
|
|
// Continue execution if we were able to recover from the crash.
|
2020-01-03 23:43:08 +03:00
|
|
|
if (js::RecoverFromCrash(GetId(), aForkId)) {
|
2019-08-04 19:55:00 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-03 03:30:39 +03:00
|
|
|
// Shut down cleanly so that we don't mask the report with our own crash.
|
2020-01-03 23:43:08 +03:00
|
|
|
Shutdown();
|
2018-07-22 14:49:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Handling Channel Messages
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// When messages are received from child processes, we want their handler to
|
|
|
|
// execute on the main thread. The main thread might be blocked in WaitUntil,
|
|
|
|
// so runnables associated with child processes have special handling.
|
|
|
|
|
|
|
|
// All messages received on a channel thread which the main thread has not
|
|
|
|
// processed yet. This is protected by gMonitor.
|
|
|
|
struct PendingMessage {
|
2020-01-03 23:43:08 +03:00
|
|
|
size_t mChildId = 0;
|
2018-12-28 02:32:36 +03:00
|
|
|
Message::UniquePtr mMsg;
|
|
|
|
|
2020-01-03 23:43:08 +03:00
|
|
|
PendingMessage() {}
|
2018-12-28 02:32:36 +03:00
|
|
|
|
|
|
|
PendingMessage& operator=(PendingMessage&& aOther) {
|
2020-01-03 23:43:08 +03:00
|
|
|
mChildId = aOther.mChildId;
|
2018-12-28 02:32:36 +03:00
|
|
|
mMsg = std::move(aOther.mMsg);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
PendingMessage(PendingMessage&& aOther) { *this = std::move(aOther); }
|
2018-07-22 14:49:17 +03:00
|
|
|
};
|
|
|
|
static StaticInfallibleVector<PendingMessage> gPendingMessages;
|
|
|
|
|
2018-12-28 02:32:36 +03:00
|
|
|
static Message::UniquePtr ExtractChildMessage(ChildProcessInfo** aProcess) {
|
2018-07-22 14:49:17 +03:00
|
|
|
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
|
|
|
|
2020-01-03 23:43:08 +03:00
|
|
|
if (!gPendingMessages.length()) {
|
|
|
|
return nullptr;
|
2019-11-02 01:50:37 +03:00
|
|
|
}
|
2018-12-28 02:32:36 +03:00
|
|
|
|
2020-01-03 23:43:08 +03:00
|
|
|
PendingMessage& pending = gPendingMessages[0];
|
|
|
|
*aProcess = GetChildProcess(pending.mChildId);
|
|
|
|
MOZ_RELEASE_ASSERT(*aProcess);
|
2018-07-22 14:49:17 +03:00
|
|
|
|
2020-01-03 23:43:08 +03:00
|
|
|
Message::UniquePtr msg = std::move(pending.mMsg);
|
|
|
|
gPendingMessages.erase(&pending);
|
2019-11-02 01:50:37 +03:00
|
|
|
|
2020-01-03 23:43:08 +03:00
|
|
|
return msg;
|
2020-01-03 19:43:40 +03:00
|
|
|
}
|
2019-11-02 01:50:37 +03:00
|
|
|
|
2020-01-03 23:43:08 +03:00
|
|
|
/* static */
|
|
|
|
void ChildProcessInfo::MaybeProcessNextMessage() {
|
2020-01-03 19:43:40 +03:00
|
|
|
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
|
|
|
|
2020-01-03 23:43:08 +03:00
|
|
|
Maybe<MonitorAutoLock> lock;
|
|
|
|
lock.emplace(*gMonitor);
|
2020-01-03 19:43:40 +03:00
|
|
|
|
2020-01-03 23:43:08 +03:00
|
|
|
MaybeHandlePendingSyncMessage();
|
2020-01-03 19:43:40 +03:00
|
|
|
|
2020-01-03 23:43:08 +03:00
|
|
|
ChildProcessInfo* process;
|
|
|
|
Message::UniquePtr msg = ExtractChildMessage(&process);
|
2020-01-03 19:43:40 +03:00
|
|
|
|
2020-01-03 23:43:08 +03:00
|
|
|
if (msg) {
|
|
|
|
lock.reset();
|
|
|
|
process->OnIncomingMessage(*msg);
|
|
|
|
} else {
|
|
|
|
// We wait for at most one second before returning to the caller.
|
|
|
|
TimeStamp deadline = TimeStamp::Now() + TimeDuration::FromSeconds(1);
|
|
|
|
gMonitor->WaitUntil(deadline);
|
2018-07-22 14:49:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-02 01:50:37 +03:00
|
|
|
// Whether there is a pending task on the main thread's message loop to handle
|
|
|
|
// all pending messages.
|
|
|
|
static bool gHasPendingMessageRunnable;
|
|
|
|
|
2018-07-22 14:49:17 +03:00
|
|
|
// Runnable created on the main thread to handle any tasks sent by the replay
|
|
|
|
// message loop thread which were not handled while the main thread was blocked.
|
2019-02-26 01:12:51 +03:00
|
|
|
/* static */
|
|
|
|
void ChildProcessInfo::MaybeProcessPendingMessageRunnable() {
|
2018-07-22 14:49:17 +03:00
|
|
|
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
|
|
|
MonitorAutoLock lock(*gMonitor);
|
|
|
|
MOZ_RELEASE_ASSERT(gHasPendingMessageRunnable);
|
|
|
|
gHasPendingMessageRunnable = false;
|
2018-12-28 02:32:36 +03:00
|
|
|
while (true) {
|
|
|
|
ChildProcessInfo* process = nullptr;
|
|
|
|
Message::UniquePtr msg = ExtractChildMessage(&process);
|
|
|
|
|
|
|
|
if (msg) {
|
|
|
|
MonitorAutoUnlock unlock(*gMonitor);
|
2019-05-13 02:16:36 +03:00
|
|
|
process->OnIncomingMessage(*msg);
|
2018-12-28 02:32:36 +03:00
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
2018-07-22 14:49:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Execute a task that processes a message received from the child. This is
|
|
|
|
// called on a channel thread, and the function executes asynchronously on
|
|
|
|
// the main thread.
|
2020-01-03 23:43:08 +03:00
|
|
|
/* static */ void ChildProcessInfo::ReceiveChildMessageOnMainThread(
|
|
|
|
size_t aChildId, Message::UniquePtr aMsg) {
|
2018-07-22 14:49:17 +03:00
|
|
|
MOZ_RELEASE_ASSERT(!NS_IsMainThread());
|
|
|
|
|
|
|
|
MonitorAutoLock lock(*gMonitor);
|
|
|
|
|
|
|
|
PendingMessage pending;
|
2020-01-03 23:43:08 +03:00
|
|
|
pending.mChildId = aChildId;
|
2018-12-28 02:32:36 +03:00
|
|
|
pending.mMsg = std::move(aMsg);
|
|
|
|
gPendingMessages.append(std::move(pending));
|
2018-07-22 14:49:17 +03:00
|
|
|
|
2018-12-28 02:32:36 +03:00
|
|
|
// Notify the main thread, if it is waiting in WaitUntilPaused.
|
2018-07-22 14:49:17 +03:00
|
|
|
gMonitor->NotifyAll();
|
|
|
|
|
|
|
|
// Make sure there is a task on the main thread's message loop that can
|
|
|
|
// process this task if necessary.
|
|
|
|
if (!gHasPendingMessageRunnable) {
|
|
|
|
gHasPendingMessageRunnable = true;
|
|
|
|
MainThreadMessageLoop()->PostTask(
|
|
|
|
NewRunnableFunction("MaybeProcessPendingMessageRunnable",
|
|
|
|
MaybeProcessPendingMessageRunnable));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace parent
|
|
|
|
} // namespace recordreplay
|
|
|
|
} // namespace mozilla
|