/* -*- 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/. */
#ifdef XP_UNIX // { # include "mozilla/Preferences.h" # include <fcntl.h> # include <unistd.h> # include <sys/stat.h>
usingnamespace mozilla;
/* * The following code supports triggering a registered callback upon * receiving a specific signal. * * Take about:memory for example, we register * 1. doGCCCDump for doMemoryReport * 2. doMemoryReport for sDumpAboutMemorySignum(SIGRTMIN) * and sDumpAboutMemoryAfterMMUSignum(SIGRTMIN+1). * * When we receive one of these signals, we write the signal number to a pipe. * The IO thread then notices that the pipe has been written to, and kicks off * the appropriate task on the main thread. * * This scheme is similar to using signalfd(), except it's portable and it * doesn't require the use of sigprocmask, which is problematic because it * masks signals received by child processes.
*/
// This is the write-end of a pipe that we use to notice when a // specific signal occurs. static Atomic<int> sDumpPipeWriteFd(-1);
// Implementations may call this function multiple times if they ensure that // it's safe to call OpenFd() multiple times and they call StopWatching() // first. void FdWatcher::StartWatching() {
MOZ_ASSERT(XRE_GetAsyncIOEventTarget()->IsOnCurrentThread());
MOZ_ASSERT(mFd == -1);
// Since implementations can call StartWatching() multiple times, they can of // course call StopWatching() multiple times. void FdWatcher::StopWatching() {
MOZ_ASSERT(XRE_GetAsyncIOEventTarget()->IsOnCurrentThread());
if (aSignal) { if (sigaction(aSignal, &action, nullptr)) {
LOG("SignalPipeWatcher failed to register sig %d.", aSignal);
}
} else {
MutexAutoLock lock(mSignalInfoLock); for (SignalInfoArray::index_type i = 0; i < mSignalInfo.Length(); i++) { if (sigaction(mSignalInfo[i].mSignal, &action, nullptr)) {
LOG("SignalPipeWatcher failed to register signal(%d) " "dump signal handler.",
mSignalInfo[i].mSignal);
}
}
}
}
SignalPipeWatcher::~SignalPipeWatcher() { if (sDumpPipeWriteFd != -1) {
StopWatching();
}
}
int SignalPipeWatcher::OpenFd() {
MOZ_ASSERT(XRE_GetAsyncIOEventTarget()->IsOnCurrentThread());
// Create a pipe. When we receive a signal in our signal handler, we'll // write the signum to the write-end of this pipe. int pipeFds[2]; if (pipe(pipeFds)) {
LOG("SignalPipeWatcher failed to create pipe."); return -1;
}
// Close this pipe on calls to exec().
fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC);
fcntl(pipeFds[1], F_SETFD, FD_CLOEXEC);
int readFd = pipeFds[0];
sDumpPipeWriteFd = pipeFds[1];
// Close sDumpPipeWriteFd /after/ setting the fd to -1. // Otherwise we have the (admittedly far-fetched) race where we // // 1) close sDumpPipeWriteFd // 2) open a new fd with the same number as sDumpPipeWriteFd // had. // 3) receive a signal, then write to the fd. int pipeWriteFd = sDumpPipeWriteFd.exchange(-1);
close(pipeWriteFd);
for (FifoInfoArray::index_type i = 0; i < mFifoInfo.Length(); ++i) { if (mFifoInfo[i].mCommand.Equals(aCommand)) {
LOG("Register command(%s) callback failed! (DUPLICATE)", aCommand.get()); return;
}
}
FifoInfo aFifoInfo = {aCommand, aCallback};
mFifoInfo.AppendElement(aFifoInfo);
}
FifoWatcher::~FifoWatcher() = default;
int FifoWatcher::OpenFd() { // If the memory_info_dumper.directory pref is specified, put the fifo // there. Otherwise, put it into the system's tmp directory.
nsCOMPtr<nsIFile> file;
nsresult rv; if (mDirPath.Length() > 0) {
rv = XRE_GetFileFromPath(mDirPath.get(), getter_AddRefs(file)); if (NS_FAILED(rv)) {
LOG("FifoWatcher failed to open file \"%s\"", mDirPath.get()); return -1;
}
} else {
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(file)); if (NS_WARN_IF(NS_FAILED(rv))) { return -1;
}
}
rv = file->AppendNative("debug_info_trigger"_ns); if (NS_WARN_IF(NS_FAILED(rv))) { return -1;
}
// unlink might fail because the file doesn't exist, or for other reasons. // But we don't care it fails; any problems will be detected later, when we // try to mkfifo or open the file. if (unlink(path.get())) {
LOG("FifoWatcher::OpenFifo unlink failed; errno=%d. " "Continuing despite error.",
errno);
}
# ifdef ANDROID // Android runs with a umask, so we need to chmod our fifo to make it // world-writable.
chmod(path.get(), 0666); # endif
int fd; do { // The fifo will block until someone else has written to it. In // particular, open() will block until someone else has opened it for // writing! We want open() to succeed and read() to block, so we open // with NONBLOCK and then fcntl that away.
fd = open(path.get(), O_RDONLY | O_NONBLOCK);
} while (fd == -1 && errno == EINTR);
if (fd == -1) {
LOG("FifoWatcher::OpenFifo open failed; errno=%d", errno); return -1;
}
// Make fd blocking now that we've opened it. if (fcntl(fd, F_SETFL, 0)) {
close(fd); return -1;
}
char buf[1024]; int nread; do { // sizeof(buf) - 1 to leave space for the null-terminator.
nread = read(aFd, buf, sizeof(buf));
} while (nread == -1 && errno == EINTR);
if (nread == -1) { // We want to avoid getting into a situation where // OnFileCanReadWithoutBlocking is called in an infinite loop, so when // something goes wrong, stop watching the fifo altogether.
LOG("FifoWatcher hit an error (%d) and is quitting.", errno);
StopWatching(); return;
}
if (nread == 0) { // If we get EOF, that means that the other side closed the fifo. We need // to close and re-open the fifo; if we don't, // OnFileCanWriteWithoutBlocking will be called in an infinite loop.
LOG("FifoWatcher closing and re-opening fifo.");
StopWatching();
StartWatching(); return;
}
// Trimming whitespace is important because if you do // |echo "foo" >> debug_info_trigger|, // it'll actually write "foo\n" to the fifo.
inputStr.Trim("\b\t\r\n");
{
MutexAutoLock lock(mFifoInfoLock);
for (FifoInfoArray::index_type i = 0; i < mFifoInfo.Length(); i++) { const nsCString commandStr = mFifoInfo[i].mCommand; if (inputStr == commandStr.get()) {
mFifoInfo[i].mCallback(inputStr); return;
}
}
}
LOG("Got unexpected value from fifo; ignoring it.");
}
#endif// XP_UNIX }
// In Android case, this function will open a file named aFilename under // /data/local/tmp/"aFoldername". // Otherwise, it will open a file named aFilename under "NS_OS_TEMP_DIR". /* static */
nsresult nsDumpUtils::OpenTempFile(const nsACString& aFilename, nsIFile** aFile, const nsACString& aFoldername, Mode aMode) { #ifdef ANDROID // For Android, first try the downloads directory which is world-readable // rather than the temp directory which is not. if (!*aFile) { if (char* env = PR_GetEnv("DOWNLOADS_DIRECTORY")) {
Unused << NS_WARN_IF(
NS_FAILED(NS_NewNativeLocalFile(nsCString(env), aFile)));
}
} #endif
nsresult rv; if (!*aFile) { if (NS_IsMainThread()) { // This allows tests to override, but isn't safe off-mainthread.
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, aFile);
} else {
rv = GetSpecialSystemDirectory(OS_TemporaryDirectory, aFile);
} if (NS_WARN_IF(NS_FAILED(rv))) { return rv;
}
}
#ifdef ANDROID // /data/local/tmp is a true tmp directory; anyone can create a file there, // but only the user which created the file can remove it. We want non-root // users to be able to remove these files, so we write them into a // subdirectory of the temp directory and chmod 777 that directory. if (!aFoldername.IsEmpty()) {
rv = (*aFile)->AppendNative(aFoldername); if (NS_WARN_IF(NS_FAILED(rv))) { return rv;
}
// It's OK if this fails; that probably just means that the directory // already exists.
Unused << (*aFile)->Create(nsIFile::DIRECTORY_TYPE, 0777);
#ifdef ANDROID // Make this file world-read/writable; the permissions passed to the // CreateUnique call above are not sufficient on Android, which runs with a // umask.
nsAutoCString path;
rv = file->GetNativePath(path); if (NS_WARN_IF(NS_FAILED(rv))) { return rv;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.