Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(70)

Side by Side Diff: src/procfs_byte_counter.cc

Issue 5180003: cashew: add local byte counters (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/cashew.git@master
Patch Set: Address jglasgow and zelidrag (offline) code review comments Created 10 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/procfs_byte_counter.h ('k') | src/procfs_byte_counter_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/procfs_byte_counter.h"
6
7 #include <stdio.h>
8
9 #include <glog/logging.h>
10
11 namespace cashew {
12
13 static const char *kProcNetDevPath = "/proc/net/dev";
14 static const int kLineBufferSize = 256;
15
16 // /proc/net/dev output format
17 // must remain consistent with kernel (see net/core/dev.c)
18 static const char *kProcNetDevExpectedLine1 =
19 "Inter-| Receive "
20 " | Transmit\n";
21 static const char *kProcNetDevExpectedLine2 =
22 " face |bytes packets errs drop fifo frame "
23 "compressed multicast|bytes packets errs "
24 "drop fifo colls carrier compressed\n";
25 static const char *kProcNetDevLineFormat =
26 "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n";
27 static const int kProcNetDevLineFormatNumElements = 17;
28
29 // ReadStats timer interval
30 // TODO(vlaviano): can reduce this once we implement a smarter ShouldEmitSignal
31 // policy than 'return true'
32 static const guint kReadStatsIntervalSeconds = 60;
33
34 ProcfsByteCounter::ProcfsByteCounter(const std::string& interface)
35 : interface_(interface), proc_net_dev_fp_(NULL), read_stats_source_id_(0),
36 counter_rx_bytes_(0), counter_tx_bytes_(0), baseline_stats_(),
37 baseline_initialized_(false), delegate_(NULL) {
38 DCHECK(!interface_.empty());
39 // initialize our counter state
40 if (!ReadStats()) {
41 LOG(WARNING) << interface_ << ": ctor: ReadStats failed";
42 // fall through so that we'll try again later
43 }
44 // schedule periodic ReadStats() calls to update counter state in the future
45 read_stats_source_id_ = g_timeout_add_seconds(kReadStatsIntervalSeconds,
46 StaticReadStatsCallback, this);
47 if (read_stats_source_id_ == 0) {
48 LOG(ERROR) << interface_ << ": ctor: g_idle_add failed";
49 return;
50 }
51 }
52
53 ProcfsByteCounter::~ProcfsByteCounter() {
54 DCHECK(proc_net_dev_fp_ == NULL);
55 if (read_stats_source_id_ != 0 && !g_source_remove(read_stats_source_id_)) {
56 DLOG(WARNING) << interface_ << ": dtor: g_source_remove_failed";
57 }
58 }
59
60 const std::string& ProcfsByteCounter::GetInterface() const {
61 return interface_;
62 }
63
64 uint64 ProcfsByteCounter::GetRxBytes() const {
65 return counter_rx_bytes_;
66 }
67
68 uint64 ProcfsByteCounter::GetTxBytes() const {
69 return counter_tx_bytes_;
70 }
71
72 void ProcfsByteCounter::SetDelegate(ByteCounterDelegate *delegate) {
73 DLOG(INFO) << interface_ << ": SetDelegate";
74 delegate_ = delegate;
75 }
76
77 // private methods
78
79 bool ProcfsByteCounter::OpenProcNetDev() {
80 DCHECK(proc_net_dev_fp_ == NULL);
81 if ((proc_net_dev_fp_ = fopen(kProcNetDevPath, "r")) == NULL) {
82 PLOG(ERROR) << interface_ << ": OpenProcNetDev: couldn't open "
83 << kProcNetDevPath;
84 return false;
85 }
86 return true;
87 }
88
89 void ProcfsByteCounter::CloseProcNetDev() {
90 if (proc_net_dev_fp_ != NULL) {
91 if (fclose(proc_net_dev_fp_)) {
92 PLOG(WARNING) << interface_ << ": CloseProcNetDev: fclose failed";
93 }
94 proc_net_dev_fp_ = NULL;
95 }
96 DCHECK(proc_net_dev_fp_ == NULL);
97 }
98
99 // static
100 gboolean ProcfsByteCounter::StaticReadStatsCallback(gpointer data) {
101 ProcfsByteCounter *counter = reinterpret_cast<ProcfsByteCounter*>(data);
102 CHECK_NOTNULL(counter);
103 if (!counter->ReadStats()) {
104 DLOG(WARNING) << counter->GetInterface() << ": StaticReadStatsCallback: "
105 << "ReadStats failed";
106 }
107 return TRUE; // we want to be called again later
108 }
109
110 bool ProcfsByteCounter::ReadStats() {
111 // we open and close the virtual file each time through, because we get stale
112 // data if we keep the file open and rewind/read repeatedly
113 if (!OpenProcNetDev()) {
114 LOG(ERROR) << interface_ << ": ReadStats: OpenProcDevNet failed";
115 return false;
116 }
117 char line_buffer[kLineBufferSize];
118 for (int line_number = 1; // use 1-based counting to make log msgs sensible
119 fgets(line_buffer, sizeof(line_buffer), proc_net_dev_fp_) != NULL;
120 ++line_number) {
121 // the first two lines contain human-friendly column headers
122 // skip them, while ensuring that they are as we expect
123 // this provides some confidence that the format hasn't changed on us
124 if (line_number == 1 || line_number == 2) {
125 if (strncmp(line_buffer, (line_number == 1) ? kProcNetDevExpectedLine1 :
126 kProcNetDevExpectedLine2, sizeof(line_buffer))) {
127 LOG(ERROR) << interface_ << ": ReadStats: line " << line_number
128 << " is not as we expect";
129 CloseProcNetDev();
130 return false;
131 }
132 continue;
133 }
134 // each subsequent line contains the name and stats for a network interface
135 // parse it, reading stats, and see if the name matches |interface_|
136 //
137 // replace first colon in string (immediately after device name) with a
138 // space so that sscanf greedy string parsing doesn't cause the colon and
139 // possibly the rx byte count following it to all be consumed as part of the
140 // device name.
141 char *colon_ptr = strchr(line_buffer, ':');
142 if (colon_ptr == NULL) {
143 LOG(ERROR) << interface_ << ": ReadStats: line " << line_number
144 << ": strchr failed to find colon";
145 CloseProcNetDev();
146 return false;
147 }
148 *colon_ptr = ' ';
149 char device_name[IFNAMSIZ];
150 struct net_device_stats stats;
151 memset(&stats, 0, sizeof(stats));
152 // cpplint tells us that sscanf, while not prohibited, is sketchy
153 // we'll risk it since this data is from the kernel proc fs and we provide
154 // the format string
155 //
156 // note that some fields of net_device_stats are combined when producing the
157 // contents of /proc/net/dev
158 // we assign these combined values to the field named in the column header
159 int result = sscanf(line_buffer, kProcNetDevLineFormat, // NOLINT
160 device_name, &stats.rx_bytes, &stats.rx_packets,
161 &stats.rx_errors, &stats.rx_dropped,
162 &stats.rx_fifo_errors, &stats.rx_frame_errors,
163 &stats.rx_compressed, &stats.multicast, &stats.tx_bytes,
164 &stats.tx_packets, &stats.tx_errors, &stats.tx_dropped,
165 &stats.tx_fifo_errors, &stats.collisions,
166 &stats.tx_carrier_errors, &stats.tx_compressed);
167 if (result == EOF && ferror(proc_net_dev_fp_)) {
168 PLOG(ERROR) << interface_ << ": ReadStats: line " << line_number
169 << ": sscanf failed";
170 CloseProcNetDev();
171 return false;
172 }
173 if (result != kProcNetDevLineFormatNumElements) {
174 LOG(ERROR) << interface_ << ": ReadStats: line " << line_number
175 << ": sscanf matched " << ((result == EOF) ? 0 : result)
176 << " elements (" << kProcNetDevLineFormatNumElements << " expected)";
177 CloseProcNetDev();
178 return false;
179 }
180 if (device_name != interface_) {
181 continue;
182 }
183 // we found a match
184 // update our counter based on new stats
185 OnStatsUpdate(stats);
186 CloseProcNetDev();
187 return true;
188 }
189 LOG(ERROR) << interface_ << ": ReadStats: no matching device";
190 CloseProcNetDev();
191 return false;
192 }
193
194 void ProcfsByteCounter::OnStatsUpdate(const struct net_device_stats& stats) {
195 DLOG(INFO) << interface_ << ": OnStatsUpdate: stats: rx_bytes = "
196 << stats.rx_bytes << ", tx_bytes = " << stats.tx_bytes;
197 if (baseline_initialized_) {
198 DLOG(INFO) << interface_ << ": OnStatsUpdate: baseline: rx_bytes = "
199 << baseline_stats_.rx_bytes << ", tx_bytes = "
200 << baseline_stats_.tx_bytes;
201 uint64 old_counter_rx_bytes = counter_rx_bytes_;
202 uint64 old_counter_tx_bytes = counter_tx_bytes_;
203 DLOG(INFO) << interface_ << ": OnStatsUpdate: old counter: rx_bytes = "
204 << old_counter_rx_bytes << ", tx_bytes = " << old_counter_tx_bytes;
205 // these seemingly naive computations handle wraparound due to the modular
206 // nature of unsigned subtraction
207 counter_rx_bytes_ += stats.rx_bytes - baseline_stats_.rx_bytes;
208 counter_tx_bytes_ += stats.tx_bytes - baseline_stats_.tx_bytes;
209 DLOG(INFO) << interface_ << ": OnStatsUpdate: new counter: rx_bytes = "
210 << counter_rx_bytes_ << ", tx_bytes = " << counter_tx_bytes_;
211 bool counter_changed = counter_rx_bytes_ != old_counter_rx_bytes ||
212 counter_tx_bytes_ != old_counter_tx_bytes;
213 if (delegate_ != NULL && counter_changed) {
214 delegate_->OnByteCounterUpdate(this, counter_rx_bytes_,
215 counter_tx_bytes_);
216 }
217 } else {
218 baseline_initialized_ = true;
219 }
220 // We update the baseline on each new sample so that we aren't confused about
221 // how many times the device counter has wrapped around since we've started.
222 // This works as long as the sample interval is short enough to prevent wrap
223 // around between consecutive samples.
224 baseline_stats_ = stats;
225 }
226
227 } // namespace cashew
OLDNEW
« no previous file with comments | « src/procfs_byte_counter.h ('k') | src/procfs_byte_counter_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698