summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorahendrickson@chromium.org <ahendrickson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-23 20:38:20 +0000
committerahendrickson@chromium.org <ahendrickson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-23 20:38:20 +0000
commit210564f22a7eeacb57b9fe62f254cda5c2e6722b (patch)
treeb62db0d21d520d79a9d5f45c900a6c44337b4757 /net
parent71751eb28961e96a0cbf4d91f02610329f1c87ce (diff)
downloadchromium_src-210564f22a7eeacb57b9fe62f254cda5c2e6722b.zip
chromium_src-210564f22a7eeacb57b9fe62f254cda5c2e6722b.tar.gz
chromium_src-210564f22a7eeacb57b9fe62f254cda5c2e6722b.tar.bz2
Record UMA statistics for file_stream operations.
Allows control over whether or not to record the statistics based on flags. BUG=None TEST=None Review URL: http://codereview.chromium.org/7583049 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@102560 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/base/file_stream.h4
-rw-r--r--net/base/file_stream_metrics.cc81
-rw-r--r--net/base/file_stream_metrics.h41
-rw-r--r--net/base/file_stream_metrics_posix.cc21
-rw-r--r--net/base/file_stream_metrics_win.cc146
-rw-r--r--net/base/file_stream_posix.cc74
-rw-r--r--net/base/file_stream_win.cc66
-rw-r--r--net/base/net_errors_posix.cc9
-rw-r--r--net/base/net_errors_win.cc3
-rw-r--r--net/net.gyp4
10 files changed, 410 insertions, 39 deletions
diff --git a/net/base/file_stream.h b/net/base/file_stream.h
index a58557c..0a800b4 100644
--- a/net/base/file_stream.h
+++ b/net/base/file_stream.h
@@ -132,6 +132,9 @@ class NET_EXPORT FileStream {
// This method should not be called if the stream was opened READ_ONLY.
virtual int Flush();
+ // Turns on UMA error statistics gathering.
+ void EnableErrorStatistics();
+
private:
class AsyncContext;
friend class AsyncContext;
@@ -143,6 +146,7 @@ class NET_EXPORT FileStream {
base::PlatformFile file_;
int open_flags_;
bool auto_closed_;
+ bool record_uma_;
DISALLOW_COPY_AND_ASSIGN(FileStream);
};
diff --git a/net/base/file_stream_metrics.cc b/net/base/file_stream_metrics.cc
new file mode 100644
index 0000000..7ffa96d
--- /dev/null
+++ b/net/base/file_stream_metrics.cc
@@ -0,0 +1,81 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/base/file_stream_metrics.h"
+
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+
+namespace net {
+
+namespace {
+
+void RecordFileErrorTypeCount(FileErrorSource source) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "Net.FileErrorType_Counts", source, FILE_ERROR_SOURCE_COUNT);
+}
+
+} // namespace
+
+void RecordFileError(int error, FileErrorSource source, bool record) {
+ LOG(ERROR) << " " << __FUNCTION__ << "()"
+ << " error = " << error
+ << " source = " << source
+ << " record = " << record;
+
+ if (!record)
+ return;
+
+ RecordFileErrorTypeCount(source);
+
+ int bucket = GetFileErrorUmaBucket(error);
+
+ // Fixed values per platform.
+ static const int max_bucket = MaxFileErrorUmaBucket();
+ static const int max_error = MaxFileErrorUmaValue();
+
+ switch(source) {
+ case FILE_ERROR_SOURCE_OPEN:
+ UMA_HISTOGRAM_ENUMERATION("Net.FileError_Open", error, max_error);
+ UMA_HISTOGRAM_ENUMERATION("Net.FileErrorRange_Open", bucket, max_bucket);
+ break;
+
+ case FILE_ERROR_SOURCE_WRITE:
+ UMA_HISTOGRAM_ENUMERATION("Net.FileError_Write", error, max_error);
+ UMA_HISTOGRAM_ENUMERATION("Net.FileErrorRange_Write", bucket, max_bucket);
+ break;
+
+ case FILE_ERROR_SOURCE_READ:
+ UMA_HISTOGRAM_ENUMERATION("Net.FileError_Read", error, max_error);
+ UMA_HISTOGRAM_ENUMERATION("Net.FileErrorRange_Read", bucket, max_bucket);
+ break;
+
+ case FILE_ERROR_SOURCE_SEEK:
+ UMA_HISTOGRAM_ENUMERATION("Net.FileError_Seek", error, max_error);
+ UMA_HISTOGRAM_ENUMERATION("Net.FileErrorRange_Seek", bucket, max_bucket);
+ break;
+
+ case FILE_ERROR_SOURCE_FLUSH:
+ UMA_HISTOGRAM_ENUMERATION("Net.FileError_Flush", error, max_error);
+ UMA_HISTOGRAM_ENUMERATION("Net.FileErrorRange_Flush", bucket, max_bucket);
+ break;
+
+ case FILE_ERROR_SOURCE_SET_EOF:
+ UMA_HISTOGRAM_ENUMERATION("Net.FileError_SetEof", error, max_error);
+ UMA_HISTOGRAM_ENUMERATION("Net.FileErrorRange_SetEof", bucket,
+ max_bucket);
+ break;
+
+ case FILE_ERROR_SOURCE_GET_SIZE:
+ UMA_HISTOGRAM_ENUMERATION("Net.FileError_GetSize", error, max_error);
+ UMA_HISTOGRAM_ENUMERATION("Net.FileErrorRange_GetSize", bucket,
+ max_bucket);
+ break;
+
+ default:
+ break;
+ }
+}
+
+} // namespace net
diff --git a/net/base/file_stream_metrics.h b/net/base/file_stream_metrics.h
new file mode 100644
index 0000000..cdf16e5
--- /dev/null
+++ b/net/base/file_stream_metrics.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// File error statistics gathering.
+
+#ifndef NET_BASE_FILE_STREAM_METRICS_H_
+#define NET_BASE_FILE_STREAM_METRICS_H_
+#pragma once
+
+namespace net {
+
+enum FileErrorSource {
+ FILE_ERROR_SOURCE_OPEN = 0,
+ FILE_ERROR_SOURCE_WRITE,
+ FILE_ERROR_SOURCE_READ,
+ FILE_ERROR_SOURCE_SEEK,
+ FILE_ERROR_SOURCE_FLUSH,
+ FILE_ERROR_SOURCE_SET_EOF,
+ FILE_ERROR_SOURCE_GET_SIZE,
+ FILE_ERROR_SOURCE_COUNT,
+};
+
+// UMA error statistics gathering.
+// Put the error value into a bucket.
+int GetFileErrorUmaBucket(int error);
+
+// The largest bucket number, plus 1.
+int MaxFileErrorUmaBucket();
+
+// The highest error value we want to individually report.
+int MaxFileErrorUmaValue();
+
+// |error| is a platform-specific error (Windows or Posix).
+// |source| indicates the operation that resulted in the error.
+// |record| is a flag indicating that we are interested in this error.
+void RecordFileError(int error, FileErrorSource source, bool record);
+
+} // namespace net
+
+#endif // NET_BASE_FILE_STREAM_METRICS_H_
diff --git a/net/base/file_stream_metrics_posix.cc b/net/base/file_stream_metrics_posix.cc
new file mode 100644
index 0000000..7407d50
--- /dev/null
+++ b/net/base/file_stream_metrics_posix.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/base/file_stream_metrics.h"
+
+namespace net {
+
+int GetFileErrorUmaBucket(int error) {
+ return 1;
+}
+
+int MaxFileErrorUmaBucket() {
+ return 2;
+}
+
+int MaxFileErrorUmaValue() {
+ return 160;
+}
+
+} // namespace net
diff --git a/net/base/file_stream_metrics_win.cc b/net/base/file_stream_metrics_win.cc
new file mode 100644
index 0000000..c397e4b
--- /dev/null
+++ b/net/base/file_stream_metrics_win.cc
@@ -0,0 +1,146 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/base/file_stream_metrics.h"
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+
+namespace net {
+
+namespace {
+
+struct Range {
+ int low;
+ int high;
+};
+
+// The error range list is extracted from WinError.h.
+//
+// NOTE: The gaps between the ranges need to be recorded too.
+// They will have odd-numbered buckets.
+const Range kErrorRangeList[] = {
+ { 0, 321 }, // 2.
+ { 335, 371 }, // 4.
+ { 383, 387 }, // 6.
+ { 399, 404 }, // etc.
+ { 415, 418 },
+ { 431, 433 },
+ { 447, 868 },
+ { 994, 1471 },
+ { 1500, 1513 },
+ { 1536, 1553 },
+ { 1601, 1654 },
+ { 1700, 1834 },
+ { 1898, 1938 },
+ { 2000, 2024 },
+ { 2048, 2085 },
+ { 2108, 2110 },
+ { 2202, 2203 },
+ { 2250, 2251 },
+ { 2401, 2405 },
+ { 3000, 3021 },
+ { 3950, 3951 },
+ { 4000, 4007 },
+ { 4050, 4066 },
+ { 4096, 4116 },
+ { 4200, 4215 },
+ { 4300, 4353 },
+ { 4390, 4395 },
+ { 4500, 4501 },
+ { 4864, 4905 },
+ { 5001, 5090 },
+ { 5890, 5953 },
+ { 6000, 6023 },
+ { 6118, 6119 },
+ { 6200, 6201 },
+ { 6600, 6649 },
+ { 6700, 6732 },
+ { 6800, 6856 },
+ { 7001, 7071 },
+ { 8001, 8018 },
+ { 8192, 8263 },
+ { 8301, 8640 },
+ { 8704, 8705 },
+ { 8960, 9053 },
+ { 9216, 9218 },
+ { 9263, 9276 },
+ { 9472, 9506 },
+ { 9550, 9573 },
+ { 9600, 9622 },
+ { 9650, 9656 },
+ { 9688, 9723 },
+ { 9750, 9754 },
+ { 9800, 9802 },
+ { 9850, 9853 },
+ { 9900, 9907 },
+ { 10000, 10072 },
+ { 10091, 10113 },
+ { 11001, 11034 },
+ { 12288, 12335 },
+ { 12544, 12559 },
+ { 12595, 12597 },
+ { 12801, 12803 },
+ { 13000, 13026 },
+ { 13800, 13933 },
+ { 14000, 14111 },
+ { 15000, 15039 },
+ { 15080, 15086 },
+ { 15100, 15109 },
+ { 15200, 15208 },
+ { 15250, 15251 },
+ { 15299, 15302 },
+ { 16385, 16436 },
+ { 18432, 18454 },
+ { 20480, 20486 },
+ { 24577, 24607 },
+ { 28673, 28698 },
+ { 32790, 32816 },
+ { 33281, 33322 },
+ { 35005, 35024 },
+ { 36000, 36004 },
+ { 40010, 40011 },
+ { 40067, 40069 },
+ { 53248, 53293 },
+ { 53376, 53382 },
+ { 57344, 57360 },
+ { 57377, 57394 },
+ { 65535, 65536 } // 2 * kNumErrorRanges.
+};
+const size_t kNumErrorRanges = ARRAYSIZE_UNSAFE(kErrorRangeList);
+
+} // namespace
+
+// Windows has very many errors. We're not interested in most of them, but we
+// don't know which ones are significant.
+// This function maps error ranges to specific buckets.
+// If we get hits on the buckets, we can add those values to the values we
+// record individually.
+// If we get values *between* the buckets, we record those as buckets too.
+int GetFileErrorUmaBucket(int error) {
+ error = HRESULT_CODE(error);
+
+ // This is a linear search, but of a short fixed-size array.
+ // It also gets called infrequently, on errors.
+ for (size_t n = 0; n < kNumErrorRanges; ++n) {
+ if (error < kErrorRangeList[n].low)
+ return (2 * (n + 1)) - 1; // In gap before the range.
+ if (error <= kErrorRangeList[n].high)
+ return 2 * (n + 1); // In the range.
+ }
+
+ // After the last bucket.
+ return 2 * kNumErrorRanges + 1;
+}
+
+int MaxFileErrorUmaBucket() {
+ return 2 * kNumErrorRanges + 2;
+}
+
+int MaxFileErrorUmaValue() {
+ return kErrorRangeList[0].high + 1;
+}
+
+} // namespace net
diff --git a/net/base/file_stream_posix.cc b/net/base/file_stream_posix.cc
index 8625016..7a7cbf3 100644
--- a/net/base/file_stream_posix.cc
+++ b/net/base/file_stream_posix.cc
@@ -25,6 +25,7 @@
#include "base/threading/thread_restrictions.h"
#include "base/threading/worker_pool.h"
#include "base/synchronization/waitable_event.h"
+#include "net/base/file_stream_metrics.h"
#include "net/base/net_errors.h"
#if defined(OS_ANDROID)
@@ -47,52 +48,59 @@ COMPILE_ASSERT(FROM_BEGIN == SEEK_SET &&
namespace {
+int RecordAndMapError(int error, FileErrorSource source, bool record_uma) {
+ RecordFileError(error, source, record_uma);
+ return MapSystemError(error);
+}
+
// ReadFile() is a simple wrapper around read() that handles EINTR signals and
// calls MapSystemError() to map errno to net error codes.
-int ReadFile(base::PlatformFile file, char* buf, int buf_len) {
+int ReadFile(base::PlatformFile file, char* buf, int buf_len, bool record_uma) {
base::ThreadRestrictions::AssertIOAllowed();
// read(..., 0) returns 0 to indicate end-of-file.
// Loop in the case of getting interrupted by a signal.
ssize_t res = HANDLE_EINTR(read(file, buf, static_cast<size_t>(buf_len)));
if (res == static_cast<ssize_t>(-1))
- return MapSystemError(errno);
+ RecordAndMapError(errno, FILE_ERROR_SOURCE_READ, record_uma);
return static_cast<int>(res);
}
void ReadFileTask(base::PlatformFile file,
char* buf,
int buf_len,
+ bool record_uma,
CompletionCallback* callback) {
- callback->Run(ReadFile(file, buf, buf_len));
+ callback->Run(ReadFile(file, buf, buf_len, record_uma));
}
// WriteFile() is a simple wrapper around write() that handles EINTR signals and
// calls MapSystemError() to map errno to net error codes. It tries to write to
// completion.
-int WriteFile(base::PlatformFile file, const char* buf, int buf_len) {
+int WriteFile(base::PlatformFile file, const char* buf, int buf_len,
+ bool record_uma) {
base::ThreadRestrictions::AssertIOAllowed();
ssize_t res = HANDLE_EINTR(write(file, buf, buf_len));
if (res == -1)
- return MapSystemError(errno);
+ RecordAndMapError(errno, FILE_ERROR_SOURCE_WRITE, record_uma);
return res;
}
void WriteFileTask(base::PlatformFile file,
const char* buf,
- int buf_len,
+ int buf_len, bool record_uma,
CompletionCallback* callback) {
- callback->Run(WriteFile(file, buf, buf_len));
+ callback->Run(WriteFile(file, buf, buf_len, record_uma));
}
// FlushFile() is a simple wrapper around fsync() that handles EINTR signals and
// calls MapSystemError() to map errno to net error codes. It tries to flush to
// completion.
-int FlushFile(base::PlatformFile file) {
+int FlushFile(base::PlatformFile file, bool record_uma) {
base::ThreadRestrictions::AssertIOAllowed();
ssize_t res = HANDLE_EINTR(fsync(file));
if (res == -1)
- return MapSystemError(errno);
+ RecordAndMapError(errno, FILE_ERROR_SOURCE_FLUSH, record_uma);
return res;
}
@@ -144,6 +152,10 @@ class FileStream::AsyncContext {
// |result| is the result of the Read/Write task.
void OnBackgroundIOCompleted(int result);
+ void EnableErrorStatistics() {
+ record_uma_ = true;
+ }
+
private:
// Always called on the IO thread, either directly by a task on the
// MessageLoop or by ~AsyncContext().
@@ -167,6 +179,7 @@ class FileStream::AsyncContext {
CancelableCallbackTask* message_loop_task_;
bool is_closing_;
+ bool record_uma_;
DISALLOW_COPY_AND_ASSIGN(AsyncContext);
};
@@ -178,7 +191,8 @@ FileStream::AsyncContext::AsyncContext()
this, &AsyncContext::OnBackgroundIOCompleted),
background_io_completed_(true, false),
message_loop_task_(NULL),
- is_closing_(false) {}
+ is_closing_(false),
+ record_uma_(false) {}
FileStream::AsyncContext::~AsyncContext() {
is_closing_ = true;
@@ -207,6 +221,7 @@ void FileStream::AsyncContext::InitiateAsyncRead(
NewRunnableFunction(
&ReadFileTask,
file, buf, buf_len,
+ record_uma_,
&background_io_completed_callback_),
true /* task_is_slow */);
}
@@ -221,6 +236,7 @@ void FileStream::AsyncContext::InitiateAsyncWrite(
NewRunnableFunction(
&WriteFileTask,
file, buf, buf_len,
+ record_uma_,
&background_io_completed_callback_),
true /* task_is_slow */);
}
@@ -261,14 +277,16 @@ void FileStream::AsyncContext::RunAsynchronousCallback() {
FileStream::FileStream()
: file_(base::kInvalidPlatformFileValue),
open_flags_(0),
- auto_closed_(true) {
+ auto_closed_(true),
+ record_uma_(false) {
DCHECK(!IsOpen());
}
FileStream::FileStream(base::PlatformFile file, int flags)
: file_(file),
open_flags_(flags),
- auto_closed_(false) {
+ auto_closed_(false),
+ record_uma_(false) {
// If the file handle is opened with base::PLATFORM_FILE_ASYNC, we need to
// make sure we will perform asynchronous File IO to it.
if (flags & base::PLATFORM_FILE_ASYNC) {
@@ -301,13 +319,11 @@ int FileStream::Open(const FilePath& path, int open_flags) {
open_flags_ = open_flags;
file_ = base::CreatePlatformFile(path, open_flags_, NULL, NULL);
- if (file_ == base::kInvalidPlatformFileValue) {
- return MapSystemError(errno);
- }
+ if (file_ == base::kInvalidPlatformFileValue)
+ return RecordAndMapError(errno, FILE_ERROR_SOURCE_OPEN, record_uma_);
- if (open_flags_ & base::PLATFORM_FILE_ASYNC) {
+ if (open_flags_ & base::PLATFORM_FILE_ASYNC)
async_context_.reset(new AsyncContext());
- }
return OK;
}
@@ -328,7 +344,7 @@ int64 FileStream::Seek(Whence whence, int64 offset) {
off_t res = lseek(file_, static_cast<off_t>(offset),
static_cast<int>(whence));
if (res == static_cast<off_t>(-1))
- return MapSystemError(errno);
+ return RecordAndMapError(errno, FILE_ERROR_SOURCE_SEEK, record_uma_);
return res;
}
@@ -345,7 +361,7 @@ int64 FileStream::Available() {
struct stat info;
if (fstat(file_, &info) != 0)
- return MapSystemError(errno);
+ return RecordAndMapError(errno, FILE_ERROR_SOURCE_GET_SIZE, record_uma_);
int64 size = static_cast<int64>(info.st_size);
DCHECK_GT(size, cur_pos);
@@ -366,10 +382,12 @@ int FileStream::Read(
DCHECK(open_flags_ & base::PLATFORM_FILE_ASYNC);
// If we're in async, make sure we don't have a request in flight.
DCHECK(!async_context_->callback());
+ if (record_uma_)
+ async_context_->EnableErrorStatistics();
async_context_->InitiateAsyncRead(file_, buf, buf_len, callback);
return ERR_IO_PENDING;
} else {
- return ReadFile(file_, buf, buf_len);
+ return ReadFile(file_, buf, buf_len, record_uma_);
}
}
@@ -406,10 +424,12 @@ int FileStream::Write(
DCHECK(open_flags_ & base::PLATFORM_FILE_ASYNC);
// If we're in async, make sure we don't have a request in flight.
DCHECK(!async_context_->callback());
+ if (record_uma_)
+ async_context_->EnableErrorStatistics();
async_context_->InitiateAsyncWrite(file_, buf, buf_len, callback);
return ERR_IO_PENDING;
} else {
- return WriteFile(file_, buf, buf_len);
+ return WriteFile(file_, buf, buf_len, record_uma_);
}
}
@@ -429,15 +449,21 @@ int64 FileStream::Truncate(int64 bytes) {
// And truncate the file.
int result = ftruncate(file_, bytes);
- return result == 0 ? seek_position :
- static_cast<int64>(MapSystemError(errno));
+ if (result == 0)
+ return seek_position;
+
+ return RecordAndMapError(errno, FILE_ERROR_SOURCE_SET_EOF, record_uma_);
}
int FileStream::Flush() {
if (!IsOpen())
return ERR_UNEXPECTED;
- return FlushFile(file_);
+ return FlushFile(file_, record_uma_);
+}
+
+void FileStream::EnableErrorStatistics() {
+ record_uma_ = true;
}
} // namespace net
diff --git a/net/base/file_stream_win.cc b/net/base/file_stream_win.cc
index 314687e..4e3e66c 100644
--- a/net/base/file_stream_win.cc
+++ b/net/base/file_stream_win.cc
@@ -11,6 +11,7 @@
#include "base/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/threading/thread_restrictions.h"
+#include "net/base/file_stream_metrics.h"
#include "net/base/net_errors.h"
namespace net {
@@ -33,12 +34,22 @@ static void IncrementOffset(OVERLAPPED* overlapped, DWORD count) {
SetOffset(overlapped, offset);
}
+namespace {
+
+int RecordAndMapError(int error, FileErrorSource source, bool record_uma) {
+ RecordFileError(error, source, record_uma);
+ return MapSystemError(error);
+}
+
+} // namespace
+
// FileStream::AsyncContext ----------------------------------------------
class FileStream::AsyncContext : public MessageLoopForIO::IOHandler {
public:
AsyncContext(FileStream* owner)
- : owner_(owner), context_(), callback_(NULL), is_closing_(false) {
+ : owner_(owner), context_(), callback_(NULL), is_closing_(false),
+ record_uma_(false), error_source_(FILE_ERROR_SOURCE_COUNT) {
context_.handler = this;
}
~AsyncContext();
@@ -48,6 +59,12 @@ class FileStream::AsyncContext : public MessageLoopForIO::IOHandler {
OVERLAPPED* overlapped() { return &context_.overlapped; }
CompletionCallback* callback() const { return callback_; }
+ void set_error_source(FileErrorSource source) { error_source_ = source; }
+
+ void EnableErrorStatistics() {
+ record_uma_ = true;
+ }
+
private:
virtual void OnIOCompleted(MessageLoopForIO::IOContext* context,
DWORD bytes_read, DWORD error);
@@ -56,6 +73,8 @@ class FileStream::AsyncContext : public MessageLoopForIO::IOHandler {
MessageLoopForIO::IOContext context_;
CompletionCallback* callback_;
bool is_closing_;
+ bool record_uma_;
+ FileErrorSource error_source_;
};
FileStream::AsyncContext::~AsyncContext() {
@@ -91,7 +110,7 @@ void FileStream::AsyncContext::OnIOCompleted(
int result = static_cast<int>(bytes_read);
if (error && error != ERROR_HANDLE_EOF)
- result = MapSystemError(error);
+ result = RecordAndMapError(error, error_source_, record_uma_);
if (bytes_read)
IncrementOffset(&context->overlapped, bytes_read);
@@ -106,13 +125,15 @@ void FileStream::AsyncContext::OnIOCompleted(
FileStream::FileStream()
: file_(INVALID_HANDLE_VALUE),
open_flags_(0),
- auto_closed_(true) {
+ auto_closed_(true),
+ record_uma_(false) {
}
FileStream::FileStream(base::PlatformFile file, int flags)
: file_(file),
open_flags_(flags),
- auto_closed_(false) {
+ auto_closed_(false),
+ record_uma_(false) {
// If the file handle is opened with base::PLATFORM_FILE_ASYNC, we need to
// make sure we will perform asynchronous File IO to it.
if (flags & base::PLATFORM_FILE_ASYNC) {
@@ -149,11 +170,13 @@ int FileStream::Open(const FilePath& path, int open_flags) {
if (file_ == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
LOG(WARNING) << "Failed to open file: " << error;
- return MapSystemError(error);
+ return RecordAndMapError(error, FILE_ERROR_SOURCE_OPEN, record_uma_);
}
if (open_flags_ & base::PLATFORM_FILE_ASYNC) {
async_context_.reset(new AsyncContext(this));
+ if (record_uma_)
+ async_context_->EnableErrorStatistics();
MessageLoopForIO::current()->RegisterIOHandler(file_,
async_context_.get());
}
@@ -168,6 +191,7 @@ bool FileStream::IsOpen() const {
int64 FileStream::Seek(Whence whence, int64 offset) {
if (!IsOpen())
return ERR_UNEXPECTED;
+
DCHECK(!async_context_.get() || !async_context_->callback());
LARGE_INTEGER distance, result;
@@ -176,10 +200,12 @@ int64 FileStream::Seek(Whence whence, int64 offset) {
if (!SetFilePointerEx(file_, distance, &result, move_method)) {
DWORD error = GetLastError();
LOG(WARNING) << "SetFilePointerEx failed: " << error;
- return MapSystemError(error);
+ return RecordAndMapError(error, FILE_ERROR_SOURCE_SEEK, record_uma_);
}
- if (async_context_.get())
+ if (async_context_.get()) {
+ async_context_->set_error_source(FILE_ERROR_SOURCE_SEEK);
SetOffset(async_context_->overlapped(), result);
+ }
return result.QuadPart;
}
@@ -197,7 +223,7 @@ int64 FileStream::Available() {
if (!GetFileSizeEx(file_, &file_size)) {
DWORD error = GetLastError();
LOG(WARNING) << "GetFileSizeEx failed: " << error;
- return MapSystemError(error);
+ return RecordAndMapError(error, FILE_ERROR_SOURCE_GET_SIZE, record_uma_);
}
return file_size.QuadPart - cur_pos;
@@ -207,6 +233,7 @@ int FileStream::Read(
char* buf, int buf_len, CompletionCallback* callback) {
if (!IsOpen())
return ERR_UNEXPECTED;
+
DCHECK(open_flags_ & base::PLATFORM_FILE_READ);
OVERLAPPED* overlapped = NULL;
@@ -214,6 +241,7 @@ int FileStream::Read(
DCHECK(callback);
DCHECK(!async_context_->callback());
overlapped = async_context_->overlapped();
+ async_context_->set_error_source(FILE_ERROR_SOURCE_READ);
} else {
DCHECK(!callback);
base::ThreadRestrictions::AssertIOAllowed();
@@ -231,7 +259,7 @@ int FileStream::Read(
rv = 0; // Report EOF by returning 0 bytes read.
} else {
LOG(WARNING) << "ReadFile failed: " << error;
- rv = MapSystemError(error);
+ rv = RecordAndMapError(error, FILE_ERROR_SOURCE_READ, record_uma_);
}
} else if (overlapped) {
async_context_->IOCompletionIsPending(callback);
@@ -267,6 +295,7 @@ int FileStream::Write(
const char* buf, int buf_len, CompletionCallback* callback) {
if (!IsOpen())
return ERR_UNEXPECTED;
+
DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
OVERLAPPED* overlapped = NULL;
@@ -274,6 +303,7 @@ int FileStream::Write(
DCHECK(callback);
DCHECK(!async_context_->callback());
overlapped = async_context_->overlapped();
+ async_context_->set_error_source(FILE_ERROR_SOURCE_WRITE);
} else {
DCHECK(!callback);
base::ThreadRestrictions::AssertIOAllowed();
@@ -288,7 +318,7 @@ int FileStream::Write(
rv = ERR_IO_PENDING;
} else {
LOG(WARNING) << "WriteFile failed: " << error;
- rv = MapSystemError(error);
+ rv = RecordAndMapError(error, FILE_ERROR_SOURCE_WRITE, record_uma_);
}
} else if (overlapped) {
async_context_->IOCompletionIsPending(callback);
@@ -310,10 +340,9 @@ int FileStream::Flush() {
return OK;
}
- int rv;
- DWORD error = GetLastError();
- rv = MapSystemError(error);
- return rv;
+ return RecordAndMapError(GetLastError(),
+ FILE_ERROR_SOURCE_FLUSH,
+ record_uma_);
}
int64 FileStream::Truncate(int64 bytes) {
@@ -335,11 +364,18 @@ int64 FileStream::Truncate(int64 bytes) {
if (!result) {
DWORD error = GetLastError();
LOG(WARNING) << "SetEndOfFile failed: " << error;
- return MapSystemError(error);
+ return RecordAndMapError(error, FILE_ERROR_SOURCE_SET_EOF, record_uma_);
}
// Success.
return seek_position;
}
+void FileStream::EnableErrorStatistics() {
+ record_uma_ = true;
+
+ if (async_context_.get())
+ async_context_->EnableErrorStatistics();
+}
+
} // namespace net
diff --git a/net/base/net_errors_posix.cc b/net/base/net_errors_posix.cc
index 143912c..0d8b7de 100644
--- a/net/base/net_errors_posix.cc
+++ b/net/base/net_errors_posix.cc
@@ -5,12 +5,19 @@
#include "net/base/net_errors.h"
#include <errno.h>
+#include <stdlib.h>
+#include <string>
+#include <unistd.h>
#include "base/logging.h"
+#include "base/stringprintf.h"
namespace net {
Error MapSystemError(int os_error) {
+ if (os_error != 0)
+ DVLOG(2) << "Error " << os_error;
+
// There are numerous posix error codes, but these are the ones we thus far
// find interesting.
switch (os_error) {
@@ -98,6 +105,8 @@ Error MapSystemError(int os_error) {
return ERR_ACCESS_DENIED;
case EUSERS: // Too many users.
return ERR_INSUFFICIENT_RESOURCES;
+ case EMFILE: // Too many open files.
+ return ERR_INSUFFICIENT_RESOURCES;
case 0:
return OK;
diff --git a/net/base/net_errors_win.cc b/net/base/net_errors_win.cc
index fcede60..74ed228 100644
--- a/net/base/net_errors_win.cc
+++ b/net/base/net_errors_win.cc
@@ -12,6 +12,9 @@ namespace net {
// Map winsock and system errors to Chromium errors.
Error MapSystemError(int os_error) {
+ if (os_error != 0)
+ DVLOG(2) << "Error " << os_error;
+
// There are numerous Winsock error codes, but these are the ones we thus far
// find interesting.
switch (os_error) {
diff --git a/net/net.gyp b/net/net.gyp
index b4ce67a..0bc2862 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -102,6 +102,10 @@
'base/ev_root_ca_metadata.cc',
'base/ev_root_ca_metadata.h',
'base/file_stream.h',
+ 'base/file_stream_metrics.h',
+ 'base/file_stream_metrics.cc',
+ 'base/file_stream_metrics_posix.cc',
+ 'base/file_stream_metrics_win.cc',
'base/file_stream_posix.cc',
'base/file_stream_win.cc',
'base/filter.cc',