119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman//===-- llvm/Support/GCOV.h - LLVM coverage tool ----------------*- C++ -*-===// 219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman// 319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman// The LLVM Compiler Infrastructure 419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman// 519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman// This file is distributed under the University of Illinois Open Source 619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman// License. See LICENSE.TXT for details. 719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman// 819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman//===----------------------------------------------------------------------===// 919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman// 1019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman// This header provides the interface to read and write coverage files that 1119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman// use 'gcov' format. 1219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman// 1319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman//===----------------------------------------------------------------------===// 1419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 1519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman#ifndef LLVM_GCOV_H 1619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman#define LLVM_GCOV_H 1719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 1819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman#include "llvm/ADT/SmallVector.h" 1919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman#include "llvm/ADT/StringMap.h" 2019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman#include "llvm/Support/MemoryBuffer.h" 2119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman#include "llvm/Support/raw_ostream.h" 2219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 2319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumannamespace llvm { 2419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 2519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanclass GCOVFunction; 2619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanclass GCOVBlock; 2719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanclass GCOVLines; 2819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanclass FileInfo; 2919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 3019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanenum GCOVFormat { 3119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman InvalidGCOV, 3219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman GCNO_402, 3319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman GCNO_404, 3419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman GCDA_402, 3519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman GCDA_404 3619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman}; 3719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 3819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman/// GCOVBuffer - A wrapper around MemoryBuffer to provide GCOV specific 3919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman/// read operations. 4019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanclass GCOVBuffer { 4119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanpublic: 4219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman GCOVBuffer(MemoryBuffer *B) : Buffer(B), Cursor(0) {} 4319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 4419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman /// readGCOVFormat - Read GCOV signature at the beginning of buffer. 4519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman enum GCOVFormat readGCOVFormat() { 4619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman StringRef Magic = Buffer->getBuffer().slice(0, 12); 4719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Cursor = 12; 4819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman if (Magic == "oncg*404MVLL") 4919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman return GCNO_404; 5019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman else if (Magic == "oncg*204MVLL") 5119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman return GCNO_402; 5219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman else if (Magic == "adcg*404MVLL") 5319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman return GCDA_404; 5419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman else if (Magic == "adcg*204MVLL") 5519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman return GCDA_402; 5619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 5719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Cursor = 0; 5819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman return InvalidGCOV; 5919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman } 6019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 6119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman /// readFunctionTag - If cursor points to a function tag then increment the 6219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman /// cursor and return true otherwise return false. 6319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman bool readFunctionTag() { 6419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor+4); 6519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman if (Tag.empty() || 6619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Tag[0] != '\0' || Tag[1] != '\0' || 6719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Tag[2] != '\0' || Tag[3] != '\1') { 6819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman return false; 6919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman } 7019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Cursor += 4; 7119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman return true; 7219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman } 7319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 7419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman /// readBlockTag - If cursor points to a block tag then increment the 7519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman /// cursor and return true otherwise return false. 7619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman bool readBlockTag() { 7719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor+4); 7819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman if (Tag.empty() || 7919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Tag[0] != '\0' || Tag[1] != '\0' || 8019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Tag[2] != '\x41' || Tag[3] != '\x01') { 8119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman return false; 8219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman } 8319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Cursor += 4; 8419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman return true; 8519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman } 8619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 8719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman /// readEdgeTag - If cursor points to an edge tag then increment the 8819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman /// cursor and return true otherwise return false. 8919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman bool readEdgeTag() { 9019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor+4); 9119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman if (Tag.empty() || 9219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Tag[0] != '\0' || Tag[1] != '\0' || 9319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Tag[2] != '\x43' || Tag[3] != '\x01') { 9419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman return false; 9519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman } 9619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Cursor += 4; 9719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman return true; 9819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman } 9919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 10019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman /// readLineTag - If cursor points to a line tag then increment the 10119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman /// cursor and return true otherwise return false. 10219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman bool readLineTag() { 10319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor+4); 10419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman if (Tag.empty() || 10519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Tag[0] != '\0' || Tag[1] != '\0' || 10619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Tag[2] != '\x45' || Tag[3] != '\x01') { 10719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman return false; 10819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman } 10919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Cursor += 4; 11019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman return true; 11119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman } 11219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 11319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman /// readArcTag - If cursor points to an gcda arc tag then increment the 11419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman /// cursor and return true otherwise return false. 11519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman bool readArcTag() { 11619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor+4); 11719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman if (Tag.empty() || 11819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Tag[0] != '\0' || Tag[1] != '\0' || 11919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Tag[2] != '\xa1' || Tag[3] != '\1') { 12019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman return false; 12119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman } 12219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Cursor += 4; 12319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman return true; 12419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman } 12519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 12619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman uint32_t readInt() { 12719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman uint32_t Result; 12819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman StringRef Str = Buffer->getBuffer().slice(Cursor, Cursor+4); 12919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman assert (Str.empty() == false && "Unexpected memory buffer end!"); 13019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Cursor += 4; 13119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Result = *(uint32_t *)(Str.data()); 13219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman return Result; 13319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman } 13419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 13519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman uint64_t readInt64() { 13619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman uint64_t Lo = readInt(); 13719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman uint64_t Hi = readInt(); 13819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman uint64_t Result = Lo | (Hi << 32); 13919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman return Result; 14019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman } 14119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 14219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman StringRef readString() { 14319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman uint32_t Len = readInt() * 4; 14419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman StringRef Str = Buffer->getBuffer().slice(Cursor, Cursor+Len); 14519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman Cursor += Len; 14619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman return Str; 14719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman } 14819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 14919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman uint64_t getCursor() const { return Cursor; } 15019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanprivate: 15119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman MemoryBuffer *Buffer; 15219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman uint64_t Cursor; 15319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman}; 15419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 15519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman/// GCOVFile - Collects coverage information for one pair of coverage file 15619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman/// (.gcno and .gcda). 15719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanclass GCOVFile { 15819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanpublic: 15919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman GCOVFile() {} 16019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman ~GCOVFile(); 16119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman bool read(GCOVBuffer &Buffer); 16219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman void dump(); 16319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman void collectLineCounts(FileInfo &FI); 16419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanprivate: 16519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman SmallVector<GCOVFunction *, 16> Functions; 16619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman}; 16719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 16819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman/// GCOVFunction - Collects function information. 16919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanclass GCOVFunction { 17019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanpublic: 17119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman GCOVFunction() : Ident(0), LineNumber(0) {} 17219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman ~GCOVFunction(); 17319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman bool read(GCOVBuffer &Buffer, GCOVFormat Format); 17419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman void dump(); 17519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman void collectLineCounts(FileInfo &FI); 17619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanprivate: 17719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman uint32_t Ident; 17819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman uint32_t LineNumber; 17919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman StringRef Name; 18019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman StringRef Filename; 18119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman SmallVector<GCOVBlock *, 16> Blocks; 18219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman}; 18319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 18419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman/// GCOVBlock - Collects block information. 18519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanclass GCOVBlock { 18619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanpublic: 18719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman GCOVBlock(uint32_t N) : Number(N), Counter(0) {} 18819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman ~GCOVBlock(); 18919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman void addEdge(uint32_t N) { Edges.push_back(N); } 19019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman void addLine(StringRef Filename, uint32_t LineNo); 19119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman void addCount(uint64_t N) { Counter = N; } 19219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman void dump(); 19319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman void collectLineCounts(FileInfo &FI); 19419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanprivate: 19519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman uint32_t Number; 19619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman uint64_t Counter; 19719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman SmallVector<uint32_t, 16> Edges; 19819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman StringMap<GCOVLines *> Lines; 19919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman}; 20019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 20119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman/// GCOVLines - A wrapper around a vector of int to keep track of line nos. 20219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanclass GCOVLines { 20319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanpublic: 20419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman ~GCOVLines() { Lines.clear(); } 20519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman void add(uint32_t N) { Lines.push_back(N); } 20619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman void collectLineCounts(FileInfo &FI, StringRef Filename, uint32_t Count); 20719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman void dump(); 20819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 20919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanprivate: 21019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman SmallVector<uint32_t, 4> Lines; 21119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman}; 21219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 21319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumantypedef SmallVector<uint32_t, 16> LineCounts; 21419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanclass FileInfo { 21519bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanpublic: 21619bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman void addLineCount(StringRef Filename, uint32_t Line, uint32_t Count); 21719bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman void print(); 21819bac1e08be200c31efd26f0f5fd144c9b3eefd3John Baumanprivate: 21919bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman StringMap<LineCounts> LineInfo; 22019bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman}; 22119bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 22219bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman} 22319bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman 22419bac1e08be200c31efd26f0f5fd144c9b3eefd3John Bauman#endif 225