You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
599 lines
15 KiB
599 lines
15 KiB
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <tchar.h>
|
|
#include <winsock.h>
|
|
#include <bits.h>
|
|
#include <iphlpapi.h>
|
|
#include <iptypes.h>
|
|
|
|
#define WINSOCK_PORT 4000
|
|
|
|
struct BITS_INTERNAL_STATS
|
|
{
|
|
UINT64 BytesTransferred;
|
|
UINT64 BlocksTransferred;
|
|
DWORD LastBlockSize;
|
|
float LastServerSpeed;
|
|
float AverageServerSpeed;
|
|
float LastInterfaceSpeed;
|
|
float AverageInterfaceSpeed;
|
|
DWORD InterfaceId;
|
|
};
|
|
|
|
|
|
long g_PollInterval = 200;
|
|
long g_SendSize = 1000;
|
|
long g_ReceiveSize = 1000;
|
|
long g_Time = 10 * 60;
|
|
|
|
long g_StartTime;
|
|
|
|
long g_WinsockBusyTime = 10;
|
|
long g_WinsockFreeTime = 30;
|
|
TCHAR * g_WinsockServer;
|
|
SOCKET g_conn;
|
|
|
|
long g_TotalSent = 0;
|
|
long g_TotalReceived = 0;
|
|
|
|
TCHAR * g_Url;
|
|
|
|
BG_JOB_PRIORITY g_Priority = BG_JOB_PRIORITY_NORMAL;
|
|
IBackgroundCopyJob * g_job;
|
|
|
|
HANDLE g_StartEvent;
|
|
volatile BOOL g_Halt;
|
|
|
|
TCHAR * g_OutputFileName;
|
|
FILE * g_OutputFile = NULL;
|
|
|
|
MIB_IFROW g_InitialNetStats;
|
|
DWORD g_NetworkAdapterIndex = 0;
|
|
|
|
HANDLE g_BitsThread;
|
|
HANDLE g_WinsockThread;
|
|
|
|
void CollectWinsockStats();
|
|
void CollectBitsStats();
|
|
|
|
void CleanupWinsock();
|
|
void EnumerateAdapters();
|
|
|
|
//------------------------------------------------------
|
|
|
|
void Usage()
|
|
{
|
|
printf(
|
|
"parameters:\n"
|
|
" -p <msec> to poll every <msec> milliseconds\n"
|
|
" -server <name> to specify the Winsock server name or IP address\n"
|
|
" -ss <size> to send <size> bytes per winsock request\n"
|
|
" -sr <size> to receive <size> bytes per winsock request\n"
|
|
" -wb <sec> winsock thread's busy time\n"
|
|
" -wf <sec> winsock thread's free time\n"
|
|
" -t <secs> number of seconds to run test\n"
|
|
" -netindex <index> network adapter index to monitor\n"
|
|
" -url <url> URL to transfer\n"
|
|
);
|
|
}
|
|
|
|
long GetLongArg( int & i, int & argc, TCHAR * argv[], TCHAR * msg )
|
|
{
|
|
++i;
|
|
if (i < argc)
|
|
{
|
|
return _ttol( argv[i] );
|
|
}
|
|
else
|
|
{
|
|
_tprintf(msg);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
TCHAR * GetStringArg( int & i, int & argc, TCHAR * argv[], TCHAR * msg )
|
|
{
|
|
++i;
|
|
if (i < argc)
|
|
{
|
|
return argv[i];
|
|
}
|
|
else
|
|
{
|
|
_tprintf(msg);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
BG_JOB_PRIORITY GetPriority( int & i, int & argc, TCHAR * argv[], TCHAR * msg )
|
|
{
|
|
TCHAR * val = GetStringArg( i, argc, argv, msg );
|
|
|
|
if (0 == _tcsicmp( val, _T("low")))
|
|
{
|
|
return BG_JOB_PRIORITY_LOW;
|
|
}
|
|
else if (0 == _tcsicmp( val, _T("normal")))
|
|
{
|
|
return BG_JOB_PRIORITY_NORMAL;
|
|
}
|
|
else if (0 == _tcsicmp( val, _T("high")))
|
|
{
|
|
return BG_JOB_PRIORITY_HIGH;
|
|
}
|
|
else if (0 == _tcsicmp( val, _T("foreground")))
|
|
{
|
|
return BG_JOB_PRIORITY_FOREGROUND;
|
|
}
|
|
else
|
|
{
|
|
_tprintf(msg);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void ParseCmdLine( int argc, wchar_t * argv[] )
|
|
{
|
|
int i = 1;
|
|
|
|
while (i < argc)
|
|
{
|
|
if (0 == _tcsicmp( argv[i], _T("-p")))
|
|
{
|
|
g_PollInterval = GetLongArg( i, argc, argv, _T("-p must be followed by a number in milliseconds\n") );
|
|
}
|
|
else if (0 == _tcsicmp( argv[i], _T("-server")))
|
|
{
|
|
g_WinsockServer = GetStringArg( i, argc, argv, _T("-server must be followed by a server name \n"));
|
|
}
|
|
else if (0 == _tcsicmp( argv[i], _T("-ss")))
|
|
{
|
|
g_SendSize = GetLongArg( i, argc, argv, _T("-ss must be followed by a number of bytes\n"));
|
|
}
|
|
else if (0 == _tcsicmp( argv[i], _T("-sr")))
|
|
{
|
|
g_ReceiveSize = GetLongArg( i, argc, argv, _T("-sr must be followed by a number of bytes") );
|
|
}
|
|
else if (0 == _tcsicmp( argv[i], _T("-wb")))
|
|
{
|
|
g_WinsockBusyTime = GetLongArg( i, argc, argv, _T("-wb must be followed by a number of seconds\n"));
|
|
}
|
|
else if (0 == _tcsicmp( argv[i], _T("-wf")))
|
|
{
|
|
g_WinsockFreeTime = GetLongArg( i, argc, argv, _T("-wf must be followed by a number of seconds\n"));
|
|
}
|
|
else if (0 == _tcsicmp( argv[i], _T("-t")))
|
|
{
|
|
g_Time = GetLongArg( i, argc, argv, _T("-t must be followed by a time in seconds\n"));
|
|
}
|
|
else if (0 == _tcsicmp( argv[i], _T("-netindex")))
|
|
{
|
|
g_NetworkAdapterIndex = GetLongArg( i, argc, argv, _T("-netindex must be followed by a network adapter index\n"));
|
|
}
|
|
else if (0 == _tcsicmp( argv[i], _T("-f")))
|
|
{
|
|
g_OutputFileName = GetStringArg( i, argc, argv, _T("-f must be followed by a file name\n"));
|
|
}
|
|
else if (0 == _tcsicmp( argv[i], _T("-pri")))
|
|
{
|
|
g_Priority = GetPriority( i, argc, argv, _T("-pri must be followed by a priority\n"));
|
|
}
|
|
else if (0 == _tcsicmp( argv[i], _T("-url")))
|
|
{
|
|
g_Url = GetStringArg( i, argc, argv, _T("-url must be followed by an URL\n"));
|
|
}
|
|
else if (0 == _tcsicmp( argv[i], _T("-enum")))
|
|
{
|
|
EnumerateAdapters();
|
|
}
|
|
else if (0 == _tcsicmp( argv[i], _T("-?")))
|
|
{
|
|
Usage();
|
|
exit(1);
|
|
}
|
|
else
|
|
{
|
|
printf("'%S' is not a recognized option", argv[i]);
|
|
exit(1);
|
|
}
|
|
|
|
++i;
|
|
}
|
|
}
|
|
|
|
void PrepareCommon()
|
|
{
|
|
g_StartEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
|
|
if (!g_StartEvent)
|
|
{
|
|
printf("unable to create event\n");
|
|
exit(1);
|
|
}
|
|
|
|
g_Halt = FALSE;
|
|
|
|
if (g_OutputFileName)
|
|
{
|
|
g_OutputFile = _tfopen( g_OutputFileName, _T("w"));
|
|
if (!g_OutputFile)
|
|
{
|
|
printf("unable to open output file\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_OutputFile = stdout;
|
|
}
|
|
}
|
|
|
|
void CleanupCommon()
|
|
{
|
|
if (g_OutputFileName)
|
|
{
|
|
fclose( g_OutputFile );
|
|
}
|
|
}
|
|
|
|
DWORD WINAPI BitsThreadProc( PVOID arg );
|
|
|
|
void PrepareBits()
|
|
{
|
|
IBackgroundCopyManager * mgr = NULL;
|
|
|
|
HRESULT hr;
|
|
|
|
hr = CoInitialize( NULL );
|
|
if (FAILED(hr))
|
|
{
|
|
printf("unable to inti COM %x\n", hr);
|
|
exit(1);
|
|
}
|
|
|
|
|
|
hr = CoCreateInstance(
|
|
__uuidof(BackgroundCopyManager),
|
|
NULL, // no aggregation
|
|
CLSCTX_ALL,
|
|
__uuidof(IBackgroundCopyManager),
|
|
(LPVOID *) &mgr );
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
printf("unable to create BITS manager %x\n", hr);
|
|
exit(1);
|
|
}
|
|
|
|
GUID guid;
|
|
hr = mgr->CreateJob( L"perf test", BG_JOB_TYPE_DOWNLOAD, &guid, &g_job );
|
|
if (FAILED(hr))
|
|
{
|
|
printf("unable to create BITS job %x\n", hr);
|
|
exit(1);
|
|
}
|
|
|
|
hr = g_job->AddFile( g_Url, L"c:\\temp\\download.dat" );
|
|
if (FAILED(hr))
|
|
{
|
|
printf("unable to create add file to job %x\n", hr);
|
|
exit(1);
|
|
}
|
|
|
|
hr = g_job->SetPriority( g_Priority );
|
|
if (FAILED(hr))
|
|
{
|
|
printf("unable to set job priority %x\n", hr);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void CleanupBits()
|
|
{
|
|
g_job->Cancel();
|
|
}
|
|
|
|
DWORD WINAPI WinsockThreadProc( PVOID arg );
|
|
|
|
void PrepareWinsock()
|
|
{
|
|
char * AsciiServer = (char *) malloc( 1 +_tcslen( g_WinsockServer ));
|
|
|
|
for (int i = _tcslen( g_WinsockServer ); i >= 0; --i)
|
|
{
|
|
AsciiServer[i] = char(g_WinsockServer[i]);
|
|
}
|
|
|
|
//
|
|
// Get the IP address of the server.
|
|
//
|
|
ULONG addr = inet_addr( AsciiServer );
|
|
|
|
if (addr == -1)
|
|
{
|
|
struct hostent *pHostEntry = gethostbyname( AsciiServer );
|
|
|
|
if (pHostEntry == 0)
|
|
{
|
|
printf("unable to resolve the address of %s: %d\n", AsciiServer, WSAGetLastError() );
|
|
exit(1);
|
|
}
|
|
|
|
addr = *(unsigned long *)pHostEntry->h_addr;
|
|
}
|
|
|
|
struct sockaddr_in dest;
|
|
dest.sin_addr.s_addr = addr;
|
|
dest.sin_family = AF_INET;
|
|
dest.sin_port = WINSOCK_PORT;
|
|
|
|
//
|
|
// Connect to the server.
|
|
//
|
|
SOCKET s = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
|
|
if (s == INVALID_SOCKET)
|
|
{
|
|
printf("unable to create socket, %d\n", WSAGetLastError());
|
|
exit(1);
|
|
}
|
|
|
|
if (SOCKET_ERROR == connect( s, (sockaddr *)&dest, sizeof(dest)))
|
|
{
|
|
printf("unable to connect, %d\n", WSAGetLastError());
|
|
exit(1);
|
|
}
|
|
|
|
printf("winsock connected to server\n");
|
|
|
|
g_InitialNetStats.dwIndex = g_NetworkAdapterIndex;
|
|
|
|
if (g_NetworkAdapterIndex)
|
|
{
|
|
DWORD err = GetIfEntry( &g_InitialNetStats );
|
|
if (err)
|
|
{
|
|
printf("unable to collect stats from network %d: %d\n", g_NetworkAdapterIndex, err);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
DWORD id;
|
|
HANDLE h = CreateThread( NULL, 0, WinsockThreadProc, PVOID(s), 0, &id);
|
|
if (!h)
|
|
{
|
|
printf("unable to create socket thread %d\n", GetLastError());
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
DWORD WINAPI WinsockThreadProc( PVOID arg )
|
|
{
|
|
SOCKET s = SOCKET(arg);
|
|
|
|
char * buf = (char *) malloc( max( g_SendSize, g_ReceiveSize ));
|
|
|
|
printf("2: winsock thread ready to start...\n");
|
|
|
|
WaitForSingleObject( g_StartEvent, INFINITE );
|
|
|
|
//
|
|
// Send and receive data forever, with duty cycle governed by the -wb and -wf parameters.
|
|
//
|
|
DWORD Sizes[2];
|
|
Sizes[0] = g_SendSize;
|
|
Sizes[1] = g_ReceiveSize;
|
|
do
|
|
{
|
|
printf("sending...\n");
|
|
|
|
long StartTime = GetTickCount();
|
|
|
|
while (long(GetTickCount()) - StartTime < (g_WinsockBusyTime * 1000) && g_Halt == FALSE)
|
|
{
|
|
if (SOCKET_ERROR == send(s, (char *) Sizes, sizeof(Sizes), 0))
|
|
{
|
|
printf("unable to send, %d\n", WSAGetLastError());
|
|
}
|
|
|
|
if (SOCKET_ERROR == send(s, buf, g_SendSize, 0))
|
|
{
|
|
printf("unable to send, %d\n", WSAGetLastError());
|
|
}
|
|
|
|
g_TotalSent += g_SendSize + sizeof(Sizes);
|
|
|
|
long Received = 0;
|
|
|
|
do
|
|
{
|
|
int bytes = recv(s, buf, g_ReceiveSize - Received, 0);
|
|
|
|
if (bytes == 0)
|
|
{
|
|
printf("server closed the socket connection\n");
|
|
break;
|
|
}
|
|
|
|
Received += bytes;
|
|
|
|
g_TotalReceived += bytes;
|
|
}
|
|
while ( Received < g_ReceiveSize );
|
|
}
|
|
|
|
printf("2: waiting...\n");
|
|
Sleep( g_WinsockFreeTime * 1000 );
|
|
}
|
|
while ( !g_Halt );
|
|
|
|
printf("2: winsock thread exiting\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CleanupWinsock()
|
|
{
|
|
}
|
|
|
|
char * JobStateStringOf( BG_JOB_STATE state )
|
|
{
|
|
switch (state)
|
|
{
|
|
case BG_JOB_STATE_QUEUED : return "queued";
|
|
case BG_JOB_STATE_CONNECTING : return "connecting";
|
|
case BG_JOB_STATE_TRANSFERRING : return "transferring";
|
|
case BG_JOB_STATE_TRANSFERRED : return "FINISHED";
|
|
case BG_JOB_STATE_TRANSIENT_ERROR : return "trans-error";
|
|
case BG_JOB_STATE_ERROR : return "ERROR";
|
|
case BG_JOB_STATE_SUSPENDED : return "SUSPENDED";
|
|
case BG_JOB_STATE_ACKNOWLEDGED : return "ACKNOWLEDGED";
|
|
case BG_JOB_STATE_CANCELLED : return "CANCELLED";
|
|
default: return "(unknown state)";
|
|
}
|
|
}
|
|
|
|
void CollectStats()
|
|
{
|
|
HRESULT hr;
|
|
BG_JOB_STATE state;
|
|
BG_JOB_PROGRESS progress;
|
|
|
|
hr = g_job->GetState( &state );
|
|
if (hr)
|
|
{
|
|
printf("unable to get job state %x\n", hr);
|
|
return;
|
|
}
|
|
|
|
hr = g_job->GetProgress( &progress );
|
|
if (hr)
|
|
{
|
|
printf("unable to get job progress %x\n", hr);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get net card stats.
|
|
//
|
|
MIB_IFROW net = {0};
|
|
long InOctets = -1, OutOctets = -1;
|
|
|
|
if (g_NetworkAdapterIndex != 0)
|
|
{
|
|
net.dwIndex = g_NetworkAdapterIndex;
|
|
|
|
if (!GetIfEntry( &net ))
|
|
{
|
|
InOctets = net.dwInOctets - g_InitialNetStats.dwInOctets;
|
|
OutOctets = net.dwOutOctets - g_InitialNetStats.dwOutOctets;
|
|
}
|
|
}
|
|
|
|
printf("%d, BITS, %s, %I64u, sockets, %d , %d",
|
|
GetTickCount() - g_StartTime,
|
|
JobStateStringOf( state ), progress.BytesTransferred,
|
|
g_TotalSent, g_TotalReceived );
|
|
|
|
if (g_NetworkAdapterIndex != 0)
|
|
{
|
|
printf(", net, %d , %d , %d", InOctets, OutOctets, InOctets+OutOctets);
|
|
}
|
|
|
|
putchar('\n');
|
|
}
|
|
|
|
void RunTest()
|
|
{
|
|
g_job->Resume();
|
|
|
|
SetEvent( g_StartEvent );
|
|
|
|
g_StartTime = GetTickCount();
|
|
|
|
do
|
|
{
|
|
CollectStats();
|
|
Sleep( g_PollInterval );
|
|
}
|
|
while ( long(GetTickCount()) - g_StartTime < (g_Time * 1000 ));
|
|
|
|
g_Halt = TRUE;
|
|
}
|
|
|
|
void __cdecl wmain (int argc, wchar_t *argv[])
|
|
{
|
|
DWORD err;
|
|
WSADATA WsaData = {0};
|
|
|
|
if ((err = WSAStartup(0x0101, &WsaData)) != NO_ERROR)
|
|
{
|
|
printf("unable to init winsock: %d\n", err);
|
|
}
|
|
|
|
ParseCmdLine( argc, argv );
|
|
|
|
PrepareCommon();
|
|
PrepareWinsock();
|
|
PrepareBits();
|
|
|
|
Sleep(1000);
|
|
|
|
RunTest();
|
|
|
|
CleanupBits();
|
|
CleanupWinsock();
|
|
CleanupCommon();
|
|
}
|
|
|
|
char * AdapterTypeStringOf( DWORD Type )
|
|
{
|
|
switch (Type)
|
|
{
|
|
case MIB_IF_TYPE_ETHERNET: return "Ethernet";
|
|
case MIB_IF_TYPE_PPP: return "PPP dial-up";
|
|
case MIB_IF_TYPE_SLIP: return "SLIP dial-up";
|
|
case MIB_IF_TYPE_TOKENRING : return "Token-ring";
|
|
default: return "(unknown type)";
|
|
}
|
|
}
|
|
|
|
void EnumerateAdapters()
|
|
{
|
|
PIP_ADAPTER_INFO buf = 0;
|
|
DWORD s;
|
|
DWORD size = 0;
|
|
|
|
s = GetAdaptersInfo( NULL, &size );
|
|
if (s != ERROR_BUFFER_OVERFLOW)
|
|
{
|
|
printf("unable to enum adapters: %d\n", s);
|
|
exit(1);
|
|
}
|
|
|
|
buf = (PIP_ADAPTER_INFO) malloc( size );
|
|
if (!buf)
|
|
{
|
|
printf("out of memory\n");
|
|
exit(1);
|
|
}
|
|
|
|
s = GetAdaptersInfo( buf, &size );
|
|
if (s)
|
|
{
|
|
printf("unable to enum adapters: %d\n", s);
|
|
exit(1);
|
|
}
|
|
|
|
while (buf)
|
|
{
|
|
printf("%d: %s %s '%s' ",
|
|
buf->Index,
|
|
AdapterTypeStringOf( buf->Type),
|
|
buf->IpAddressList.IpAddress.String,
|
|
buf->Description);
|
|
|
|
buf = buf->Next;
|
|
}
|
|
|
|
exit(1);
|
|
}
|