--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -2,16 +2,20 @@
/* 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 <algorithm>
#include <fstream>
+#ifdef __MINGW32__
+#include <fcntl.h>
+#include <ext/stdio_filebuf.h>
+#endif
#include <prio.h>
#include <prproces.h>
#ifdef XP_LINUX
#include <time.h>
#else
#include <chrono>
#endif
@@ -230,19 +234,27 @@ TelemetryImpl::CollectReports(nsIHandleR
}
void
InitHistogramRecordingEnabled()
{
TelemetryHistogram::InitHistogramRecordingEnabled();
}
+using PathChar = filesystem::Path::value_type;
+using PathCharPtr = const PathChar*;
+using PathString = filesystem::Path::string_type;
+
static uint32_t
-ReadLastShutdownDuration(const char *filename) {
+ReadLastShutdownDuration(PathCharPtr filename) {
+#ifdef XP_WIN
+ FILE *f = _wfopen(char16ptr_t(filename), L"r");
+#else
FILE *f = fopen(filename, "r");
+#endif
if (!f) {
return 0;
}
int shutdownTime;
int r = fscanf(f, "%d\n", &shutdownTime);
fclose(f);
if (r != 1) {
@@ -276,29 +288,29 @@ GetFailedProfileLockFile(nsIFile* *aFile
(*aFile)->AppendNative(NS_LITERAL_CSTRING("Telemetry.FailedProfileLocks.txt"));
return NS_OK;
}
class nsFetchTelemetryData : public Runnable
{
public:
- nsFetchTelemetryData(const char* aShutdownTimeFilename,
+ nsFetchTelemetryData(PathCharPtr aShutdownTimeFilename,
nsIFile* aFailedProfileLockFile,
nsIFile* aProfileDir)
: mozilla::Runnable("nsFetchTelemetryData")
, mShutdownTimeFilename(aShutdownTimeFilename)
, mFailedProfileLockFile(aFailedProfileLockFile)
, mTelemetry(TelemetryImpl::sTelemetry)
, mProfileDir(aProfileDir)
{
}
private:
- const char* mShutdownTimeFilename;
+ PathCharPtr mShutdownTimeFilename;
nsCOMPtr<nsIFile> mFailedProfileLockFile;
RefPtr<TelemetryImpl> mTelemetry;
nsCOMPtr<nsIFile> mProfileDir;
public:
void MainThread() {
mTelemetry->mCachedTelemetryData = true;
for (unsigned int i = 0, n = mTelemetry->mCallbacks.Count(); i < n; ++i) {
@@ -344,38 +356,34 @@ private:
mFailedProfileLockFile->Remove(false);
return NS_OK;
}
};
static TimeStamp gRecordedShutdownStartTime;
static bool gAlreadyFreedShutdownTimeFileName = false;
-static char *gRecordedShutdownTimeFileName = nullptr;
+static PathCharPtr gRecordedShutdownTimeFileName = nullptr;
-static char *
+static PathCharPtr
GetShutdownTimeFileName()
{
if (gAlreadyFreedShutdownTimeFileName) {
return nullptr;
}
if (!gRecordedShutdownTimeFileName) {
nsCOMPtr<nsIFile> mozFile;
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mozFile));
if (!mozFile)
return nullptr;
mozFile->AppendNative(NS_LITERAL_CSTRING("Telemetry.ShutdownTime.txt"));
- nsAutoCString nativePath;
- nsresult rv = mozFile->GetNativePath(nativePath);
- if (!NS_SUCCEEDED(rv))
- return nullptr;
- gRecordedShutdownTimeFileName = PL_strdup(nativePath.get());
+ gRecordedShutdownTimeFileName = NS_strdup(mozFile->NativePath().get());
}
return gRecordedShutdownTimeFileName;
}
NS_IMETHODIMP
TelemetryImpl::GetLastShutdownDuration(uint32_t *aResult)
{
@@ -436,17 +444,17 @@ TelemetryImpl::AsyncFetchTelemetryData(n
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
if (!targetThread) {
mCachedTelemetryData = true;
aCallback->Complete();
return NS_OK;
}
// We have to get the filename from the main thread.
- const char *shutdownTimeFilename = GetShutdownTimeFileName();
+ PathCharPtr shutdownTimeFilename = GetShutdownTimeFileName();
if (!shutdownTimeFilename) {
mCachedTelemetryData = true;
aCallback->Complete();
return NS_OK;
}
nsCOMPtr<nsIFile> profileDir;
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
@@ -967,19 +975,27 @@ IsValidBreakpadId(const std::string &bre
}
}
return true;
}
// Read a stack from the given file name. In case of any error, aStack is
// unchanged.
static void
-ReadStack(const char *aFileName, Telemetry::ProcessedStack &aStack)
+ReadStack(PathCharPtr aFileName, Telemetry::ProcessedStack &aStack)
{
+#ifdef __MINGW32__
+ int fd = _wopen(static_cast<char16ptr_t>(aFileName), _O_RDONLY | _O_TEXT);
+ __gnu_cxx::stdio_filebuf<char> fb(fd, std::ios::in);
+ std::istream file(&fb);
+#elif defined(XP_WIN)
+ std::ifstream file(static_cast<char16ptr_t>(aFileName));
+#else
std::ifstream file(aFileName);
+#endif
size_t numModules;
file >> numModules;
if (file.fail()) {
return;
}
char newline = file.get();
@@ -1041,49 +1057,78 @@ ReadStack(const char *aFileName, Telemet
}
aStack = stack;
}
void
TelemetryImpl::ReadLateWritesStacks(nsIFile* aProfileDir)
{
- nsAutoCString nativePath;
- nsresult rv = aProfileDir->GetNativePath(nativePath);
- if (NS_FAILED(rv)) {
+#ifdef XP_WIN
+ nsString path = aProfileDir->NativePath();
+
+ WIN32_FIND_DATAW ent;
+ nsAutoString mask(path);
+ mask.AppendLiteral(XPCOM_FILE_PATH_SEPARATOR "*");
+ HANDLE dir = FindFirstFileW(mask.get(), &ent);
+ if (dir == INVALID_HANDLE_VALUE) {
return;
}
+ const wchar_t *prefix = L"Telemetry.LateWriteFinal-";
+ unsigned int prefixLen = wcslen(prefix);
+ do {
+ if (wcsncmp(prefix, ent.cFileName, prefixLen) != 0) {
+ continue;
+ }
+
+ nsAutoString stackPath(path);
+ stackPath.AppendLiteral(XPCOM_FILE_PATH_SEPARATOR);
+ stackPath += nsDependentString(ent.cFileName);
+
+ Telemetry::ProcessedStack stack;
+ ReadStack(stackPath.get(), stack);
+ if (stack.GetStackSize() != 0) {
+ mLateWritesStacks.AddStack(stack);
+ }
+ // Delete the file so that we don't report it again on the next run.
+ DeleteFileW(stackPath.get());
+ } while (FindNextFileW(dir, &ent));
+ FindClose(dir);
+#else
+ nsCString nativePath = aProfileDir->NativePath();
+
const char *name = nativePath.get();
PRDir *dir = PR_OpenDir(name);
if (!dir) {
return;
}
PRDirEntry *ent;
const char *prefix = "Telemetry.LateWriteFinal-";
unsigned int prefixLen = strlen(prefix);
while ((ent = PR_ReadDir(dir, PR_SKIP_NONE))) {
if (strncmp(prefix, ent->name, prefixLen) != 0) {
continue;
}
- nsAutoCString stackNativePath = nativePath;
+ nsAutoCString stackNativePath(nativePath);
stackNativePath += XPCOM_FILE_PATH_SEPARATOR;
stackNativePath += nsDependentCString(ent->name);
Telemetry::ProcessedStack stack;
ReadStack(stackNativePath.get(), stack);
if (stack.GetStackSize() != 0) {
mLateWritesStacks.AddStack(stack);
}
// Delete the file so that we don't report it again on the next run.
PR_Delete(stackNativePath.get());
}
PR_CloseDir(dir);
+#endif
}
NS_IMETHODIMP
TelemetryImpl::GetLateWrites(JSContext *cx, JS::MutableHandle<JS::Value> ret)
{
// The user must call AsyncReadTelemetryData first. We return an empty list
// instead of reporting a failure so that the rest of telemetry can uniformly
// handle the read not being available yet.
@@ -1861,53 +1906,66 @@ RecordShutdownStartTimeStamp() {
GetShutdownTimeFileName();
}
void
RecordShutdownEndTimeStamp() {
if (!gRecordedShutdownTimeFileName || gAlreadyFreedShutdownTimeFileName)
return;
- nsCString name(gRecordedShutdownTimeFileName);
- PL_strfree(gRecordedShutdownTimeFileName);
+ PathString name(gRecordedShutdownTimeFileName);
+ free(const_cast<PathChar*>(gRecordedShutdownTimeFileName));
gRecordedShutdownTimeFileName = nullptr;
gAlreadyFreedShutdownTimeFileName = true;
if (gRecordedShutdownStartTime.IsNull()) {
// If |CanRecordExtended()| is true before |AsyncFetchTelemetryData| is called and
// then disabled before shutdown, |RecordShutdownStartTimeStamp| will bail out and
// we will end up with a null |gRecordedShutdownStartTime| here. This can happen
// during tests.
return;
}
- nsCString tmpName = name;
- tmpName += ".tmp";
+ PathString tmpName = name;
+ tmpName.AppendLiteral(".tmp");
+#ifdef XP_WIN
+ FILE *f = _wfopen(tmpName.get(), L"w");
+#else
FILE *f = fopen(tmpName.get(), "w");
+#endif
if (!f)
return;
// On a normal release build this should be called just before
// calling _exit, but on a debug build or when the user forces a full
// shutdown this is called as late as possible, so we have to
// white list this write as write poisoning will be enabled.
MozillaRegisterDebugFILE(f);
TimeStamp now = TimeStamp::Now();
MOZ_ASSERT(now >= gRecordedShutdownStartTime);
TimeDuration diff = now - gRecordedShutdownStartTime;
uint32_t diff2 = diff.ToMilliseconds();
int written = fprintf(f, "%d\n", diff2);
MozillaUnRegisterDebugFILE(f);
int rv = fclose(f);
if (written < 0 || rv != 0) {
+#ifdef XP_WIN
+ DeleteFileW(tmpName.get());
+#else
PR_Delete(tmpName.get());
+#endif
return;
}
+#ifdef XP_WIN
+ DeleteFileW(name.get());
+ MoveFileW(tmpName.get(), name.get());
+#else
PR_Delete(name.get());
PR_Rename(tmpName.get(), name.get());
+#endif
}
} // namespace mozilla
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
//