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

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: 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
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 static const guint kReadStatsIntervalSeconds = 15;
31
32 ProcfsByteCounter::ProcfsByteCounter(const std::string& interface)
33 : interface_(interface), proc_net_dev_fp_(NULL), read_stats_source_id_(0),
34 counter_rx_bytes_(0), counter_tx_bytes_(0), baseline_stats_(),
35 baseline_initialized_(false), delegate_(NULL) {
36 DCHECK(!interface_.empty());
37 // initialize our counter state
38 if (!ReadStats()) {
39 LOG(WARNING) << interface_ << ": ctor: ReadStats failed";
40 // fall through so that we'll try again later
41 }
42 // schedule periodic ReadStats() calls to update counter state in the future
43 read_stats_source_id_ = g_timeout_add_seconds(kReadStatsIntervalSeconds,
44 StaticReadStatsCallback, this);
45 if (read_stats_source_id_ == 0) {
46 LOG(ERROR) << interface_ << ": ctor: g_idle_add failed";
47 return;
48 }
49 }
50
51 ProcfsByteCounter::~ProcfsByteCounter() {
52 DCHECK(proc_net_dev_fp_ == NULL);
53 if (read_stats_source_id_ != 0 && !g_source_remove(read_stats_source_id_)) {
54 DLOG(WARNING) << interface_ << ": dtor: g_source_remove_failed";
55 }
56 }
57
58 const std::string& ProcfsByteCounter::GetInterface() const {
59 return interface_;
60 }
61
62 uint64 ProcfsByteCounter::GetRxBytes() const {
63 return counter_rx_bytes_;
64 }
65
66 uint64 ProcfsByteCounter::GetTxBytes() const {
67 return counter_tx_bytes_;
68 }
69
70 void ProcfsByteCounter::SetDelegate(ByteCounterDelegate *delegate) {
71 DLOG(INFO) << interface_ << ": SetDelegate";
72 delegate_ = delegate;
73 }
74
75 // private methods
76
77 bool ProcfsByteCounter::OpenProcNetDev() {
78 DCHECK(proc_net_dev_fp_ == NULL);
79 if ((proc_net_dev_fp_ = fopen(kProcNetDevPath, "r")) == NULL) {
80 PLOG(ERROR) << interface_ << ": OpenProcNetDev: couldn't open "
81 << kProcNetDevPath;
82 return false;
83 }
84 return true;
85 }
86
87 void ProcfsByteCounter::CloseProcNetDev() {
88 if (proc_net_dev_fp_ != NULL) {
89 if (fclose(proc_net_dev_fp_)) {
90 PLOG(WARNING) << interface_ << ": CloseProcNetDev: fclose failed";
91 }
92 proc_net_dev_fp_ = NULL;
93 }
94 DCHECK(proc_net_dev_fp_ == NULL);
95 }
96
97 // static
98 gboolean ProcfsByteCounter::StaticReadStatsCallback(gpointer data) {
99 ProcfsByteCounter *counter = reinterpret_cast<ProcfsByteCounter*>(data);
100 CHECK_NOTNULL(counter);
101 if (!counter->ReadStats()) {
102 DLOG(WARNING) << counter->GetInterface() << ": StaticReadStatsCallback: "
103 << "ReadStats failed";
104 }
105 return TRUE; // we want to be called again later
106 }
107
108 bool ProcfsByteCounter::ReadStats() {
109 // we open and close the virtual file each time through, because we get stale
110 // data if we keep the file open and rewind/read repeatedly
111 if (!OpenProcNetDev()) {
112 LOG(ERROR) << interface_ << ": ReadStats: OpenProcDevNet failed";
113 return false;
114 }
115 char line_buffer[kLineBufferSize];
116 for (int line_number = 1; // use 1-based counting to make log msgs sensible
117 fgets(line_buffer, sizeof(line_buffer), proc_net_dev_fp_) != NULL;
118 ++line_number) {
119 // the first two lines contain human-friendly column headers
120 // skip them, while ensuring that they are as we expect
121 // this provides some confidence that the format hasn't changed on us
122 if (line_number == 1 || line_number == 2) {
123 if (strncmp(line_buffer, (line_number == 1) ? kProcNetDevExpectedLine1 :
124 kProcNetDevExpectedLine2, sizeof(line_buffer))) {
125 LOG(ERROR) << interface_ << ": ReadStats: line " << line_number
126 << " is not as we expect";
127 CloseProcNetDev();
128 return false;
129 }
130 continue;
131 }
132 // each subsequent line contains the name and stats for a network interface
133 // parse it, reading stats, and see if the name matches |interface_|
134 //
135 // replace first colon in string (immediately after device name) with a
136 // space so that sscanf greedy string parsing doesn't cause the colon and
137 // possibly the rx byte count following it to all be consumed as part of the
138 // device name.
139 char *colon_ptr = strchr(line_buffer, ':');
140 if (colon_ptr == NULL) {
141 LOG(ERROR) << interface_ << ": ReadStats: line " << line_number
142 << ": strchr failed to find colon";
143 CloseProcNetDev();
144 return false;
145 }
146 *colon_ptr = ' ';
147 char device_name[IFNAMSIZ];
148 struct net_device_stats stats;
149 memset(&stats, 0, sizeof(stats));
150 // cpplint tells us that sscanf, while not prohibited, is sketchy
151 // we'll risk it since this data is from the kernel proc fs and we provide
152 // the format string
153 //
154 // note that some fields of net_device_stats are combined when producing the
155 // contents of /proc/net/dev
156 // we assign these combined values to the field named in the column header
157 int result = sscanf(line_buffer, kProcNetDevLineFormat, // NOLINT
158 device_name, &stats.rx_bytes, &stats.rx_packets,
159 &stats.rx_errors, &stats.rx_dropped,
160 &stats.rx_fifo_errors, &stats.rx_frame_errors,
161 &stats.rx_compressed, &stats.multicast, &stats.tx_bytes,
162 &stats.tx_packets, &stats.tx_errors, &stats.tx_dropped,
163 &stats.tx_fifo_errors, &stats.collisions,
164 &stats.tx_carrier_errors, &stats.tx_compressed);
165 if (result == EOF && ferror(proc_net_dev_fp_)) {
166 PLOG(ERROR) << interface_ << ": ReadStats: line " << line_number
167 << ": sscanf failed";
168 CloseProcNetDev();
169 return false;
170 }
171 if (result != kProcNetDevLineFormatNumElements) {
172 LOG(ERROR) << interface_ << ": ReadStats: line " << line_number
173 << ": sscanf matched " << ((result == EOF) ? 0 : result)
174 << " elements (" << kProcNetDevLineFormatNumElements << " expected)";
175 CloseProcNetDev();
176 return false;
177 }
178 if (device_name != interface_) {
179 continue;
180 }
181 // we found a match
182 // update our counter based on new stats
183 OnStatsUpdate(stats);
184 CloseProcNetDev();
185 return true;
186 }
187 LOG(ERROR) << interface_ << ": ReadStats: no matching device";
188 CloseProcNetDev();
189 return false;
190 }
191
192 void ProcfsByteCounter::OnStatsUpdate(const struct net_device_stats& stats) {
193 DLOG(INFO) << interface_ << ": OnStatsUpdate: stats: rx_bytes = "
194 << stats.rx_bytes << ", tx_bytes = " << stats.tx_bytes;
195 if (baseline_initialized_) {
196 DLOG(INFO) << interface_ << ": OnStatsUpdate: baseline: rx_bytes = "
197 << baseline_stats_.rx_bytes << ", tx_bytes = "
198 << baseline_stats_.tx_bytes;
199 uint64 old_counter_rx_bytes = counter_rx_bytes_;
200 uint64 old_counter_tx_bytes = counter_tx_bytes_;
201 DLOG(INFO) << interface_ << ": OnStatsUpdate: old counter: rx_bytes = "
202 << old_counter_rx_bytes << ", tx_bytes = " << old_counter_tx_bytes;
203 // these seemingly naive computations handle wraparound due to the modular
204 // nature of unsigned subtraction
205 counter_rx_bytes_ += stats.rx_bytes - baseline_stats_.rx_bytes;
206 counter_tx_bytes_ += stats.tx_bytes - baseline_stats_.tx_bytes;
207 DLOG(INFO) << interface_ << ": OnStatsUpdate: new counter: rx_bytes = "
208 << counter_rx_bytes_ << ", tx_bytes = " << counter_tx_bytes_;
209 bool counter_changed = counter_rx_bytes_ != old_counter_rx_bytes ||
210 counter_tx_bytes_ != old_counter_tx_bytes;
211 if (delegate_ != NULL && counter_changed) {
212 delegate_->OnByteCounterUpdate(this, counter_rx_bytes_,
213 counter_tx_bytes_);
214 }
215 } else {
216 baseline_initialized_ = true;
217 }
218 // We update the baseline on each new sample so that we aren't confused about
219 // how many times the device counter has wrapped around since we've started.
220 // This works as long as the sample interval is short enough to prevent wrap
221 // around between consecutive samples.
222 baseline_stats_ = stats;
223 }
224
225 } // namespace cashew
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698