| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| /* |
| ** File: arena.c |
| ** Description: Testing arenas |
| ** |
| */ |
| |
| #include <string.h> |
| #include <time.h> |
| #include <stdlib.h> |
| #include "nspr.h" |
| #include "plarena.h" |
| #include "plgetopt.h" |
| |
| PRLogModuleInfo *tLM; |
| PRIntn threadCount = 0; |
| PRMonitor *tMon; |
| PRBool failed_already = PR_FALSE; |
| |
| /* Arguments from the command line with default values */ |
| PRIntn debug_mode = 0; |
| PRIntn poolMin = 4096; |
| PRIntn poolMax = (100 * 4096); |
| PRIntn arenaMin = 40; |
| PRIntn arenaMax = (100 * 40); |
| PRIntn stressIterations = 15; |
| PRIntn maxAlloc = (1024 * 1024); |
| PRIntn stressThreads = 4; |
| |
| void DumpAll( void ) |
| { |
| return; |
| } |
| |
| /* |
| ** Test Arena allocation. |
| */ |
| static void ArenaAllocate( void ) |
| { |
| PLArenaPool ap; |
| void *ptr; |
| PRInt32 i; |
| |
| PL_InitArenaPool( &ap, "AllocArena", 2048, sizeof(double)); |
| PR_LOG( tLM, PR_LOG_DEBUG, ("AA, InitPool -- Pool: %p. first: %p, current: %p, size: %d", |
| &ap, ap.first, ap.current, ap.arenasize )); |
| |
| for( i = 0; i < 150; i++ ) |
| { |
| PL_ARENA_ALLOCATE( ptr, &ap, 512 ); |
| PR_LOG( tLM, PR_LOG_DEBUG,("AA, after alloc -- Pool: %p. first: %p, current: %p, size: %d", |
| &ap, ap.first, ap.current, ap.arenasize )); |
| PR_LOG( tLM, PR_LOG_DEBUG,( |
| "AA -- Pool: %p. alloc: %p ", &ap, ptr )); |
| } |
| |
| PL_FreeArenaPool( &ap ); |
| |
| for( i = 0; i < 221; i++ ) |
| { |
| PL_ARENA_ALLOCATE( ptr, &ap, 512 ); |
| PR_LOG( tLM, PR_LOG_DEBUG,("AA, after alloc -- Pool: %p. first: %p, current: %p, size: %d", |
| &ap, ap.first, ap.current, ap.arenasize )); |
| PR_LOG( tLM, PR_LOG_DEBUG,( |
| "AA -- Pool: %p. alloc: %p ", &ap, ptr )); |
| } |
| |
| PL_FreeArenaPool( &ap ); |
| |
| return; |
| } /* end ArenaGrow() */ |
| /* |
| ** Test Arena grow. |
| */ |
| static void ArenaGrow( void ) |
| { |
| PLArenaPool ap; |
| void *ptr; |
| PRInt32 i; |
| |
| PL_InitArenaPool( &ap, "TheArena", 4096, sizeof(double)); |
| PL_ARENA_ALLOCATE( ptr, &ap, 512 ); |
| |
| PR_LOG( tLM, PR_LOG_DEBUG, ("Before growth -- Pool: %p. alloc: %p ", &ap, ptr )); |
| |
| for( i = 0; i < 10; i++ ) |
| { |
| PL_ARENA_GROW( ptr, &ap, 512, 7000 ); |
| PR_LOG( tLM, PR_LOG_DEBUG, ("After growth -- Pool: %p. alloc: %p ", &ap, ptr )); |
| } |
| |
| |
| return; |
| } /* end ArenaGrow() */ |
| |
| |
| /* |
| ** Test arena Mark and Release. |
| */ |
| static void MarkAndRelease( void ) |
| { |
| PLArenaPool ap; |
| void *ptr = NULL; |
| void *mark0, *mark1; |
| PRIntn i; |
| |
| PL_InitArenaPool( &ap, "TheArena", 4096, sizeof(double)); |
| mark0 = PL_ARENA_MARK( &ap ); |
| PR_LOG( tLM, PR_LOG_DEBUG, |
| ("mark0. ap: %p, ap.f: %p, ap.c: %p, ap.siz: %d, alloc: %p, m0: %p", |
| &ap, ap.first.next, ap.current, ap.arenasize, ptr, mark0 )); |
| |
| for( i = 0; i < 201; i++ ) |
| { |
| PL_ARENA_ALLOCATE( ptr, &ap, 512 ); |
| PR_LOG( tLM, PR_LOG_DEBUG, |
| ("mr. ap: %p, ap.f: %p, ap.c: %p, ap.siz: %d, alloc: %p", |
| &ap, ap.first.next, ap.current, ap.arenasize, ptr )); |
| } |
| |
| mark1 = PL_ARENA_MARK( &ap ); |
| PR_LOG( tLM, PR_LOG_DEBUG, |
| ("mark1. ap: %p, ap.f: %p, ap.c: %p, ap.siz: %d, alloc: %p, m1: %p", |
| &ap, ap.first.next, ap.current, ap.arenasize, ptr, mark1 )); |
| |
| |
| for( i = 0; i < 225; i++ ) |
| { |
| PL_ARENA_ALLOCATE( ptr, &ap, 512 ); |
| PR_LOG( tLM, PR_LOG_DEBUG, |
| ("mr. ap: %p, ap.f: %p, ap.c: %p, ap.siz: %d, alloc: %p", |
| &ap, ap.first.next, ap.current, ap.arenasize, ptr )); |
| } |
| |
| PL_ARENA_RELEASE( &ap, mark1 ); |
| PR_LOG( tLM, PR_LOG_DEBUG, |
| ("Release-1: %p -- Pool: %p. first: %p, current: %p, size: %d", |
| mark1, &ap, ap.first, ap.current, ap.arenasize )); |
| |
| for( i = 0; i < 20; i++ ) |
| { |
| PL_ARENA_ALLOCATE( ptr, &ap, 512 ); |
| PR_LOG( tLM, PR_LOG_DEBUG, |
| ("mr. ap: %p, ap.f: %p, ap.c: %p, ap.siz: %d, alloc: %p", |
| &ap, ap.first.next, ap.current, ap.arenasize, ptr )); |
| } |
| |
| PL_ARENA_RELEASE( &ap, mark1 ); |
| PR_LOG( tLM, PR_LOG_DEBUG, |
| ("Release-1. ap: %p, ap.f: %p, ap.c: %p, ap.siz: %d, alloc: %p", |
| &ap, ap.first.next, ap.current, ap.arenasize, ptr )); |
| |
| PL_ARENA_RELEASE( &ap, mark0 ); |
| PR_LOG( tLM, PR_LOG_DEBUG, |
| ("Release-0. ap: %p, ap.f: %p, ap.c: %p, ap.siz: %d, alloc: %p", |
| &ap, ap.first.next, ap.current, ap.arenasize, ptr )); |
| |
| PL_FreeArenaPool( &ap ); |
| PR_LOG( tLM, PR_LOG_DEBUG, |
| ("Free. ap: %p, ap.f: %p, ap.c: %p, ap.siz: %d, alloc: %p", |
| &ap, ap.first.next, ap.current, ap.arenasize, ptr )); |
| |
| PL_FinishArenaPool( &ap ); |
| PR_LOG( tLM, PR_LOG_DEBUG, |
| ("Finish. ap: %p, ap.f: %p, ap.c: %p, ap.siz: %d, alloc: %p", |
| &ap, ap.first.next, ap.current, ap.arenasize, ptr )); |
| |
| return; |
| } /* end MarkAndRelease() */ |
| |
| /* |
| ** RandSize() returns a random number in the range |
| ** min..max, rounded to the next doubleword |
| ** |
| */ |
| static PRIntn RandSize( PRIntn min, PRIntn max ) |
| { |
| PRIntn sz = (rand() % (max -min)) + min + sizeof(double); |
| |
| sz &= ~sizeof(double)-1; |
| |
| return(sz); |
| } |
| |
| |
| /* |
| ** StressThread() |
| ** A bunch of these beat on individual arenas |
| ** This tests the free_list protection. |
| ** |
| */ |
| static void PR_CALLBACK StressThread( void *arg ) |
| { |
| PLArenaPool ap; |
| PRIntn i; |
| PRIntn sz; |
| void *ptr; |
| PRThread *tp = PR_GetCurrentThread(); |
| |
| PR_LOG( tLM, PR_LOG_DEBUG, ("Stress Thread %p started\n", PR_GetCurrentThread())); |
| PL_InitArenaPool( &ap, "TheArena", RandSize( poolMin, poolMax), sizeof(double)); |
| |
| for ( i = 0; i < stressIterations; i++ ) |
| { |
| PRIntn allocated = 0; |
| |
| while ( allocated < maxAlloc ) |
| { |
| sz = RandSize( arenaMin, arenaMax ); |
| PL_ARENA_ALLOCATE( ptr, &ap, sz ); |
| if ( ptr == NULL ) |
| { |
| PR_LOG( tLM, PR_LOG_ERROR, ("ARENA_ALLOCATE() returned NULL\n\tAllocated: %d\n", allocated)); |
| break; |
| } |
| allocated += sz; |
| } |
| PR_LOG( tLM, PR_LOG_DEBUG, ("Stress thread %p finished one iteration\n", tp)); |
| PL_FreeArenaPool( &ap ); |
| } |
| PR_LOG( tLM, PR_LOG_DEBUG, ("Stress thread %p finished all iteration\n", tp)); |
| PL_FinishArenaPool( &ap ); |
| PR_LOG( tLM, PR_LOG_DEBUG, ("Stress thread %p after FinishArenaPool()\n", tp)); |
| |
| /* That's all folks! let's quit */ |
| PR_EnterMonitor(tMon); |
| threadCount--; |
| PR_Notify(tMon); |
| PR_ExitMonitor(tMon); |
| return; |
| } |
| |
| /* |
| ** Stress() |
| ** Flog the hell out of arenas multi-threaded. |
| ** Do NOT pass an individual arena to another thread. |
| ** |
| */ |
| static void Stress( void ) |
| { |
| PRThread *tt; |
| PRIntn i; |
| |
| tMon = PR_NewMonitor(); |
| |
| for ( i = 0 ; i < stressThreads ; i++ ) |
| { |
| PR_EnterMonitor(tMon); |
| tt = PR_CreateThread(PR_USER_THREAD, |
| StressThread, |
| NULL, |
| PR_PRIORITY_NORMAL, |
| PR_GLOBAL_THREAD, |
| PR_UNJOINABLE_THREAD, |
| 0); |
| threadCount++; |
| PR_ExitMonitor(tMon); |
| } |
| |
| /* Wait for all threads to exit */ |
| PR_EnterMonitor(tMon); |
| while ( threadCount != 0 ) |
| { |
| PR_Wait(tMon, PR_INTERVAL_NO_TIMEOUT); |
| } |
| PR_ExitMonitor(tMon); |
| PR_DestroyMonitor(tMon); |
| |
| return; |
| } /* end Stress() */ |
| |
| /* |
| ** EvaluateResults() |
| ** uses failed_already to display results and set program |
| ** exit code. |
| */ |
| static PRIntn EvaluateResults(void) |
| { |
| PRIntn rc = 0; |
| |
| if ( failed_already == PR_TRUE ) |
| { |
| PR_LOG( tLM, PR_LOG_DEBUG, ("FAIL\n")); |
| rc =1; |
| } |
| else |
| { |
| PR_LOG( tLM, PR_LOG_DEBUG, ("PASS\n")); |
| } |
| return(rc); |
| } /* EvaluateResults() */ |
| |
| void Help( void ) |
| { |
| printf("arena [options]\n"); |
| printf("where options are:\n"); |
| printf("-p <n> minimum size of an arena pool. Default(%d)\n", poolMin); |
| printf("-P <n> maximum size of an arena pool. Default(%d)\n", poolMax); |
| printf("-a <n> minimum size of an arena allocation. Default(%d)\n", arenaMin); |
| printf("-A <n> maximum size of an arena allocation. Default(%d)\n", arenaMax); |
| printf("-i <n> number of iterations in a stress thread. Default(%d)\n", stressIterations); |
| printf("-s <n> maximum allocation for a single stress thread. Default(%d)\n", maxAlloc); |
| printf("-t <n> number of stress threads. Default(%d)\n", stressThreads ); |
| printf("-d enable debug mode\n"); |
| printf("\n"); |
| exit(1); |
| } |
| |
| PRIntn main(PRIntn argc, char *argv[]) |
| { |
| PLOptStatus os; |
| PLOptState *opt = PL_CreateOptState(argc, argv, "dhp:P:a:A:i:s:t:"); |
| while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) |
| { |
| if (PL_OPT_BAD == os) continue; |
| switch (opt->option) |
| { |
| case 'a': /* arena Min size */ |
| arenaMin = atol( opt->value ); |
| break; |
| case 'A': /* arena Max size */ |
| arenaMax = atol( opt->value ); |
| break; |
| case 'p': /* pool Min size */ |
| poolMin = atol( opt->value ); |
| break; |
| case 'P': /* pool Max size */ |
| poolMax = atol( opt->value ); |
| break; |
| case 'i': /* Iterations in stress tests */ |
| stressIterations = atol( opt->value ); |
| break; |
| case 's': /* storage to get per iteration */ |
| maxAlloc = atol( opt->value ); |
| break; |
| case 't': /* Number of stress threads to create */ |
| stressThreads = atol( opt->value ); |
| break; |
| case 'd': /* debug mode */ |
| debug_mode = 1; |
| break; |
| case 'h': /* help */ |
| default: |
| Help(); |
| } /* end switch() */ |
| } /* end while() */ |
| PL_DestroyOptState(opt); |
| |
| srand( (unsigned)time( NULL ) ); /* seed random number generator */ |
| tLM = PR_NewLogModule("testcase"); |
| |
| |
| #if 0 |
| ArenaAllocate(); |
| ArenaGrow(); |
| #endif |
| |
| MarkAndRelease(); |
| |
| Stress(); |
| |
| return(EvaluateResults()); |
| } /* end main() */ |
| |
| /* arena.c */ |