1/* 2 * This file Copyright (C) Mnemosyne LLC 3 * 4 * This file is licensed by the GPL version 2. Works owned by the 5 * Transmission project are granted a special exemption to clause 2(b) 6 * so that the bulk of its code can remain under the MIT license. 7 * This exemption does not extend to derived works not owned by 8 * the Transmission project. 9 * 10 * $Id: stats.c 13363 2012-07-01 04:00:27Z jordan $ 11 */ 12 13#include "transmission.h" 14#include "session.h" 15#include "bencode.h" 16#include "platform.h" /* tr_sessionGetConfigDir() */ 17#include "stats.h" 18#include "utils.h" /* tr_buildPath */ 19 20/*** 21**** 22***/ 23 24static const struct tr_session_stats STATS_INIT = { 0.0f, 0, 0, 0, 0, 0 }; 25 26/** @brief Opaque, per-session data structure for bandwidth use statistics */ 27struct tr_stats_handle 28{ 29 tr_session_stats single; 30 tr_session_stats old; 31 time_t startTime; 32 bool isDirty; 33}; 34 35static char* 36getOldFilename( const tr_session * session ) 37{ 38 return tr_buildPath( tr_sessionGetConfigDir( session ), "stats.benc", NULL ); 39} 40 41static char* 42getFilename( const tr_session * session ) 43{ 44 return tr_buildPath( tr_sessionGetConfigDir( session ), "stats.json", NULL ); 45} 46 47static void 48loadCumulativeStats( const tr_session * session, 49 tr_session_stats * setme ) 50{ 51 tr_benc top; 52 char * filename; 53 bool loaded = false; 54 55 filename = getFilename( session ); 56 loaded = !tr_bencLoadFile( &top, TR_FMT_JSON, filename ); 57 tr_free( filename ); 58 59 if( !loaded ) 60 { 61 filename = getOldFilename( session ); 62 loaded = !tr_bencLoadFile( &top, TR_FMT_BENC, filename ); 63 tr_free( filename ); 64 } 65 66 if( loaded ) 67 { 68 int64_t i; 69 70 if( tr_bencDictFindInt( &top, "downloaded-bytes", &i ) ) 71 setme->downloadedBytes = (uint64_t) i; 72 if( tr_bencDictFindInt( &top, "files-added", &i ) ) 73 setme->filesAdded = (uint64_t) i; 74 if( tr_bencDictFindInt( &top, "seconds-active", &i ) ) 75 setme->secondsActive = (uint64_t) i; 76 if( tr_bencDictFindInt( &top, "session-count", &i ) ) 77 setme->sessionCount = (uint64_t) i; 78 if( tr_bencDictFindInt( &top, "uploaded-bytes", &i ) ) 79 setme->uploadedBytes = (uint64_t) i; 80 81 tr_bencFree( &top ); 82 } 83} 84 85static void 86saveCumulativeStats( const tr_session * session, 87 const tr_session_stats * s ) 88{ 89 char * filename; 90 tr_benc top; 91 92 tr_bencInitDict( &top, 5 ); 93 tr_bencDictAddInt( &top, "downloaded-bytes", s->downloadedBytes ); 94 tr_bencDictAddInt( &top, "files-added", s->filesAdded ); 95 tr_bencDictAddInt( &top, "seconds-active", s->secondsActive ); 96 tr_bencDictAddInt( &top, "session-count", s->sessionCount ); 97 tr_bencDictAddInt( &top, "uploaded-bytes", s->uploadedBytes ); 98 99 filename = getFilename( session ); 100 tr_deepLog( __FILE__, __LINE__, NULL, "Saving stats to \"%s\"", filename ); 101 tr_bencToFile( &top, TR_FMT_JSON, filename ); 102 103 tr_free( filename ); 104 tr_bencFree( &top ); 105} 106 107/*** 108**** 109***/ 110 111void 112tr_statsInit( tr_session * session ) 113{ 114 struct tr_stats_handle * stats = tr_new0( struct tr_stats_handle, 1 ); 115 116 loadCumulativeStats( session, &stats->old ); 117 stats->single.sessionCount = 1; 118 stats->startTime = tr_time( ); 119 session->sessionStats = stats; 120} 121 122static struct tr_stats_handle * 123getStats( const tr_session * session ) 124{ 125 return session ? session->sessionStats : NULL; 126} 127 128void 129tr_statsSaveDirty( tr_session * session ) 130{ 131 struct tr_stats_handle * h = getStats( session ); 132 if( ( h != NULL ) && h->isDirty ) 133 { 134 tr_session_stats cumulative = STATS_INIT; 135 tr_sessionGetCumulativeStats( session, &cumulative ); 136 saveCumulativeStats( session, &cumulative ); 137 h->isDirty = false; 138 } 139} 140 141void 142tr_statsClose( tr_session * session ) 143{ 144 tr_statsSaveDirty( session ); 145 146 tr_free( session->sessionStats ); 147 session->sessionStats = NULL; 148} 149 150/*** 151**** 152***/ 153 154static void 155updateRatio( tr_session_stats * setme ) 156{ 157 setme->ratio = tr_getRatio( setme->uploadedBytes, 158 setme->downloadedBytes ); 159} 160 161static void 162addStats( tr_session_stats * setme, 163 const tr_session_stats * a, 164 const tr_session_stats * b ) 165{ 166 setme->uploadedBytes = a->uploadedBytes + b->uploadedBytes; 167 setme->downloadedBytes = a->downloadedBytes + b->downloadedBytes; 168 setme->filesAdded = a->filesAdded + b->filesAdded; 169 setme->sessionCount = a->sessionCount + b->sessionCount; 170 setme->secondsActive = a->secondsActive + b->secondsActive; 171 updateRatio( setme ); 172} 173 174void 175tr_sessionGetStats( const tr_session * session, 176 tr_session_stats * setme ) 177{ 178 const struct tr_stats_handle * stats = getStats( session ); 179 if( stats ) 180 { 181 *setme = stats->single; 182 setme->secondsActive = tr_time( ) - stats->startTime; 183 updateRatio( setme ); 184 } 185} 186 187void 188tr_sessionGetCumulativeStats( const tr_session * session, 189 tr_session_stats * setme ) 190{ 191 const struct tr_stats_handle * stats = getStats( session ); 192 tr_session_stats current = STATS_INIT; 193 194 if( stats ) 195 { 196 tr_sessionGetStats( session, ¤t ); 197 addStats( setme, &stats->old, ¤t ); 198 } 199} 200 201void 202tr_sessionClearStats( tr_session * session ) 203{ 204 tr_session_stats zero; 205 206 zero.uploadedBytes = 0; 207 zero.downloadedBytes = 0; 208 zero.ratio = TR_RATIO_NA; 209 zero.filesAdded = 0; 210 zero.sessionCount = 0; 211 zero.secondsActive = 0; 212 213 session->sessionStats->isDirty = true; 214 session->sessionStats->single = session->sessionStats->old = zero; 215 session->sessionStats->startTime = tr_time( ); 216} 217 218/** 219*** 220**/ 221 222void 223tr_statsAddUploaded( tr_session * session, 224 uint32_t bytes ) 225{ 226 struct tr_stats_handle * s; 227 228 if( ( s = getStats( session ) ) ) 229 { 230 s->single.uploadedBytes += bytes; 231 s->isDirty = true; 232 } 233} 234 235void 236tr_statsAddDownloaded( tr_session * session, 237 uint32_t bytes ) 238{ 239 struct tr_stats_handle * s; 240 241 if( ( s = getStats( session ) ) ) 242 { 243 s->single.downloadedBytes += bytes; 244 s->isDirty = true; 245 } 246} 247 248void 249tr_statsFileCreated( tr_session * session ) 250{ 251 struct tr_stats_handle * s; 252 253 if( ( s = getStats( session ) ) ) 254 s->single.filesAdded++; 255} 256 257