1 /*
2 
3 Copyright (c) 2003-2018, Arvid Norberg
4 All rights reserved.
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9 
10     * Redistributions of source code must retain the above copyright
11       notice, this list of conditions and the following disclaimer.
12     * Redistributions in binary form must reproduce the above copyright
13       notice, this list of conditions and the following disclaimer in
14       the documentation and/or other materials provided with the distribution.
15     * Neither the name of the author nor the names of its
16       contributors may be used to endorse or promote products derived
17       from this software without specific prior written permission.
18 
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
30 
31 */
32 
33 #ifndef TORRENT_STAT_HPP_INCLUDED
34 #define TORRENT_STAT_HPP_INCLUDED
35 
36 #include <algorithm>
37 #include <limits>
38 #include <cstdint>
39 
40 #include "libtorrent/config.hpp"
41 #include "libtorrent/assert.hpp"
42 
43 namespace libtorrent {
44 
45 	class TORRENT_EXTRA_EXPORT stat_channel
46 	{
47 	public:
48 
stat_channel()49 		stat_channel()
50 			: m_total_counter(0)
51 			, m_counter(0)
52 			, m_5_sec_average(0)
53 		{}
54 
operator +=(stat_channel const & s)55 		void operator+=(stat_channel const& s)
56 		{
57 			TORRENT_ASSERT(m_counter < (std::numeric_limits<std::int32_t>::max)() - s.m_counter);
58 			m_counter += s.m_counter;
59 			TORRENT_ASSERT(m_total_counter < (std::numeric_limits<std::int64_t>::max)() - s.m_counter);
60 			m_total_counter += s.m_counter;
61 		}
62 
add(int count)63 		void add(int count)
64 		{
65 			TORRENT_ASSERT(count >= 0);
66 
67 			TORRENT_ASSERT(m_counter < (std::numeric_limits<std::int32_t>::max)() - count);
68 			m_counter += count;
69 			TORRENT_ASSERT(m_total_counter < (std::numeric_limits<std::int64_t>::max)() - count);
70 			m_total_counter += count;
71 		}
72 
73 		// should be called once every second
74 		void second_tick(int tick_interval_ms);
rate() const75 		std::int32_t rate() const { return m_5_sec_average; }
low_pass_rate() const76 		std::int32_t low_pass_rate() const { return m_5_sec_average; }
77 
total() const78 		std::int64_t total() const { return m_total_counter; }
79 
offset(std::int64_t c)80 		void offset(std::int64_t c)
81 		{
82 			TORRENT_ASSERT(m_total_counter < (std::numeric_limits<std::int64_t>::max)() - c);
83 			m_total_counter += c;
84 		}
85 
counter() const86 		std::int32_t counter() const { return m_counter; }
87 
clear()88 		void clear()
89 		{
90 			m_counter = 0;
91 			m_5_sec_average = 0;
92 			m_total_counter = 0;
93 		}
94 
95 	private:
96 
97 		// total counters
98 		std::int64_t m_total_counter;
99 
100 		// the accumulator for this second.
101 		std::int32_t m_counter;
102 
103 		// sliding average
104 		std::int32_t m_5_sec_average;
105 	};
106 
107 	class TORRENT_EXTRA_EXPORT stat
108 	{
109 	public:
operator +=(const stat & s)110 		void operator+=(const stat& s)
111 		{
112 			for (int i = 0; i < num_channels; ++i)
113 				m_stat[i] += s.m_stat[i];
114 		}
115 
sent_syn(bool ipv6)116 		void sent_syn(bool ipv6)
117 		{
118 			m_stat[upload_ip_protocol].add(ipv6 ? 60 : 40);
119 		}
120 
received_synack(bool ipv6)121 		void received_synack(bool ipv6)
122 		{
123 			// we received SYN-ACK and also sent ACK back
124 			m_stat[download_ip_protocol].add(ipv6 ? 60 : 40);
125 			m_stat[upload_ip_protocol].add(ipv6 ? 60 : 40);
126 		}
127 
received_bytes(int bytes_payload,int bytes_protocol)128 		void received_bytes(int bytes_payload, int bytes_protocol)
129 		{
130 			TORRENT_ASSERT(bytes_payload >= 0);
131 			TORRENT_ASSERT(bytes_protocol >= 0);
132 
133 			m_stat[download_payload].add(bytes_payload);
134 			m_stat[download_protocol].add(bytes_protocol);
135 		}
136 
sent_bytes(int bytes_payload,int bytes_protocol)137 		void sent_bytes(int bytes_payload, int bytes_protocol)
138 		{
139 			TORRENT_ASSERT(bytes_payload >= 0);
140 			TORRENT_ASSERT(bytes_protocol >= 0);
141 
142 			m_stat[upload_payload].add(bytes_payload);
143 			m_stat[upload_protocol].add(bytes_protocol);
144 		}
145 
146 		// and IP packet was received or sent
147 		// account for the overhead caused by it
trancieve_ip_packet(int bytes_transferred,bool ipv6)148 		void trancieve_ip_packet(int bytes_transferred, bool ipv6)
149 		{
150 			// one TCP/IP packet header for the packet
151 			// sent or received, and one for the ACK
152 			// The IPv4 header is 20 bytes
153 			// and IPv6 header is 40 bytes
154 			const int header = (ipv6 ? 40 : 20) + 20;
155 			const int mtu = 1500;
156 			const int packet_size = mtu - header;
157 			const int overhead = (std::max)(1, (bytes_transferred + packet_size - 1) / packet_size) * header;
158 			m_stat[download_ip_protocol].add(overhead);
159 			m_stat[upload_ip_protocol].add(overhead);
160 		}
161 
upload_ip_overhead() const162 		int upload_ip_overhead() const { return m_stat[upload_ip_protocol].counter(); }
download_ip_overhead() const163 		int download_ip_overhead() const { return m_stat[download_ip_protocol].counter(); }
164 
165 		// should be called once every second
second_tick(int tick_interval_ms)166 		void second_tick(int tick_interval_ms)
167 		{
168 			for (int i = 0; i < num_channels; ++i)
169 				m_stat[i].second_tick(tick_interval_ms);
170 		}
171 
low_pass_upload_rate() const172 		int low_pass_upload_rate() const
173 		{
174 			return m_stat[upload_payload].low_pass_rate()
175 				+ m_stat[upload_protocol].low_pass_rate()
176 				+ m_stat[upload_ip_protocol].low_pass_rate();
177 		}
178 
low_pass_download_rate() const179 		int low_pass_download_rate() const
180 		{
181 			return m_stat[download_payload].low_pass_rate()
182 				+ m_stat[download_protocol].low_pass_rate()
183 				+ m_stat[download_ip_protocol].low_pass_rate();
184 		}
185 
upload_rate() const186 		int upload_rate() const
187 		{
188 			return m_stat[upload_payload].rate()
189 				+ m_stat[upload_protocol].rate()
190 				+ m_stat[upload_ip_protocol].rate();
191 		}
192 
download_rate() const193 		int download_rate() const
194 		{
195 			return m_stat[download_payload].rate()
196 				+ m_stat[download_protocol].rate()
197 				+ m_stat[download_ip_protocol].rate();
198 		}
199 
total_upload() const200 		std::int64_t total_upload() const
201 		{
202 			return m_stat[upload_payload].total()
203 				+ m_stat[upload_protocol].total()
204 				+ m_stat[upload_ip_protocol].total();
205 		}
206 
total_download() const207 		std::int64_t total_download() const
208 		{
209 			return m_stat[download_payload].total()
210 				+ m_stat[download_protocol].total()
211 				+ m_stat[download_ip_protocol].total();
212 		}
213 
upload_payload_rate() const214 		int upload_payload_rate() const
215 		{ return m_stat[upload_payload].rate(); }
download_payload_rate() const216 		int download_payload_rate() const
217 		{ return m_stat[download_payload].rate(); }
218 
total_payload_upload() const219 		std::int64_t total_payload_upload() const
220 		{ return m_stat[upload_payload].total(); }
total_payload_download() const221 		std::int64_t total_payload_download() const
222 		{ return m_stat[download_payload].total(); }
223 
total_protocol_upload() const224 		std::int64_t total_protocol_upload() const
225 		{ return m_stat[upload_protocol].total(); }
total_protocol_download() const226 		std::int64_t total_protocol_download() const
227 		{ return m_stat[download_protocol].total(); }
228 
total_transfer(int channel) const229 		std::int64_t total_transfer(int channel) const
230 		{ return m_stat[channel].total(); }
transfer_rate(int channel) const231 		int transfer_rate(int channel) const
232 		{ return m_stat[channel].rate(); }
233 
234 		// this is used to offset the statistics when a
235 		// peer_connection is opened and have some previous
236 		// transfers from earlier connections.
add_stat(std::int64_t downloaded,std::int64_t uploaded)237 		void add_stat(std::int64_t downloaded, std::int64_t uploaded)
238 		{
239 			m_stat[download_payload].offset(downloaded);
240 			m_stat[upload_payload].offset(uploaded);
241 		}
242 
last_payload_downloaded() const243 		int last_payload_downloaded() const
244 		{ return m_stat[download_payload].counter(); }
last_payload_uploaded() const245 		int last_payload_uploaded() const
246 		{ return m_stat[upload_payload].counter(); }
last_protocol_downloaded() const247 		int last_protocol_downloaded() const
248 		{ return m_stat[download_protocol].counter(); }
last_protocol_uploaded() const249 		int last_protocol_uploaded() const
250 		{ return m_stat[upload_protocol].counter(); }
251 
252 		// these are the channels we keep stats for
253 		enum
254 		{
255 			// TODO: 3 everything but payload counters and rates could probably be
256 			// removed from here
257 			upload_payload,
258 			upload_protocol,
259 			download_payload,
260 			download_protocol,
261 			upload_ip_protocol,
262 			download_ip_protocol,
263 			num_channels
264 		};
265 
clear()266 		void clear()
267 		{
268 			for (int i = 0; i < num_channels; ++i)
269 				m_stat[i].clear();
270 		}
271 
operator [](int i) const272 		stat_channel const& operator[](int i) const
273 		{
274 			TORRENT_ASSERT(i >= 0 && i < num_channels);
275 			return m_stat[i];
276 		}
277 
278 	private:
279 
280 		stat_channel m_stat[num_channels];
281 	};
282 
283 }
284 
285 #endif // TORRENT_STAT_HPP_INCLUDED
286