What is a thunk table in relation to the import address table that's used in EXE files to import functions used in external DLLs?
Is this thunk table just a table containing 'Thunks' to other functions?
Thunks are a part of the Import table (IMAGE_DIRECTORY_ENTRY_IMPORT) and Delay Import Table (IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT). They are described http://msdn.microsoft.com/en-us/library/ms809762.aspx.
I'll look at my old source code and will post later a working code which dump both this tables inclusive binding information.
UPDATED:
Here is a code which I fond in one of my old program. It support only 32-bit PE, but can be easy modified to 64-bit. By the way you can see, that it dump also binding information.To test this bind the PE which you want to dump with respect of bind.exe (use for example, bind.exe -u -v Test.dll).
The code consist from about 1000 lines, so I could not post it here. I receive an error message
Oops! Your edit couldn't be submitted because:
body is limited to 30000 characters; you entered 55095
So I placed it here: http://www.ok-soft-gmbh.com/ForStackOverflow/PEInfo.c. I hope the code will help you better as a long description.
UPDATED 2: I see that my old answer is not good for searching engine. So I includes the part of the code of PEInfo.c (the functions DumpImports and DumpExports) below:
void MakeIdent (UINT nOffset)
{
for (; nOffset; nOffset--)
printf (" "); // 4 blanks
}
void DumpDword (UINT nOffset, LPCSTR pszPrefix, DWORD dw)
{
MakeIdent(nOffset);
if (dw < 100)
printf ("%s: %d\n", pszPrefix, dw);
else if (dw%(256*256) == 0)
printf ("%s: 0x%X\n", pszPrefix, dw);
else
printf ("%s: %d (0x%X)\n", pszPrefix, dw, dw);
}
void DumpTimeDateStamp (UINT nOffset, LPCSTR pszTimeDateStampPrefix, DWORD dwTimeDateStamp)
{
//struct tm tmTime;//= localtime_s ((time_t *)&dwTimeDateStamp);
//errno_t err = localtime_s (&tmTime, ((time_t *)&dwTimeDateStamp));
struct tm *ptmTime = _localtime32 ((__time32_t *)&dwTimeDateStamp);
SYSTEMTIME stSystemTime;
static CHAR szString[128];
stSystemTime.wYear = (WORD)(1900 + ptmTime->tm_year);
stSystemTime.wMonth = (WORD)(ptmTime->tm_mon + 1);
stSystemTime.wDay = (WORD)ptmTime->tm_mday;
stSystemTime.wDayOfWeek = (WORD)(ptmTime->tm_wday + 1);
stSystemTime.wHour = (WORD)ptmTime->tm_hour;
stSystemTime.wMinute = (WORD)ptmTime->tm_min;
stSystemTime.wSecond = (WORD)ptmTime->tm_sec;
stSystemTime.wMilliseconds = 0;
MakeIdent(nOffset);
printf ("%s: 0x%8X (", pszTimeDateStampPrefix, dwTimeDateStamp);
if (GetDateFormatA (LOCALE_USER_DEFAULT, 0, &stSystemTime, NULL,
szString, sizeof(szString)/sizeof(TCHAR)) != 0) {
printf (szString);
}
if (GetTimeFormatA (LOCALE_USER_DEFAULT, 0, &stSystemTime, NULL,
szString, sizeof(szString)/sizeof(TCHAR)) != 0) {
if (szString[0] != 0)
printf (" ");
printf (szString);
}
printf (")\n");
}
void DumpImports (UINT nOffset, IMAGE_OPTIONAL_HEADER32 *pOptionalHeader, PBYTE pbyFile,
IMAGE_SECTION_HEADER *pSectionHeader, IMAGE_NT_HEADERS32 *pNtHeader) // header of the section, which contains export section
{
IMAGE_IMPORT_DESCRIPTOR *pImportDescriptor = (IMAGE_IMPORT_DESCRIPTOR *)((PBYTE)pbyFile + pSectionHeader->PointerToRawData +
pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress - pSectionHeader->VirtualAddress);
DWORD dwBoundImportVA = pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress;
IMAGE_BOUND_IMPORT_DESCRIPTOR *pFirstBoundImportDescriptor = NULL, *pBoundImportDescriptor;
//DumpDword (nOffset, TEXT("Characteristics"), pImportDescriptor->Characteristics);
if (dwBoundImportVA) {
UINT i;
IMAGE_SECTION_HEADER *pFirstSectionHeader = (IMAGE_SECTION_HEADER *)((PBYTE)pOptionalHeader + //sizeof(IMAGE_OPTIONAL_HEADER32));
pNtHeader->FileHeader.SizeOfOptionalHeader);
for (i=0; i<pNtHeader->FileHeader.NumberOfSections; i++) {
if (pFirstSectionHeader[i].VirtualAddress <= dwBoundImportVA &&
dwBoundImportVA < pFirstSectionHeader[i].VirtualAddress + pFirstSectionHeader[i].Misc.VirtualSize) {
pFirstBoundImportDescriptor = (IMAGE_BOUND_IMPORT_DESCRIPTOR *)((PBYTE)pbyFile + pFirstSectionHeader[i].PointerToRawData +
dwBoundImportVA - pFirstSectionHeader[i].VirtualAddress);
break;
}
}
if (i >= pNtHeader->FileHeader.NumberOfSections)
pFirstBoundImportDescriptor = (IMAGE_BOUND_IMPORT_DESCRIPTOR *)((PBYTE)pbyFile + dwBoundImportVA);
}
for (;pImportDescriptor->Characteristics; pImportDescriptor++) {
IMAGE_THUNK_DATA *pOriginalFirstThunk = (IMAGE_THUNK_DATA *)((PBYTE)pbyFile + pSectionHeader->PointerToRawData +
pImportDescriptor->OriginalFirstThunk - pSectionHeader->VirtualAddress);
IMAGE_THUNK_DATA *pFirstThunk = (IMAGE_THUNK_DATA *)((PBYTE)pbyFile + pSectionHeader->PointerToRawData +
pImportDescriptor->FirstThunk - pSectionHeader->VirtualAddress);
IMAGE_THUNK_DATA *pOriginalThunk, *pThunk;
MakeIdent(nOffset);
printf ("%s ", pbyFile + pSectionHeader->PointerToRawData + pImportDescriptor->Name - pSectionHeader->VirtualAddress);
//DumpDword (nOffset, TEXT("Ordinal Base"), pExportDirectory->Base);
if (pImportDescriptor->TimeDateStamp == 0) {
//MakeIdent(nOffset);
printf ("(DLL is Not bound)\n");
}
else if (pImportDescriptor->TimeDateStamp == -1) {
//if bound, and real date\time stamp
// // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
//MakeIdent(nOffset);
printf ("(DLL bound with New BIND)\n");
}
else {
//MakeIdent(nOffset);
printf ("(DLL bound with Old BIND) ");
DumpTimeDateStamp (nOffset, "TimeDateStamp", pImportDescriptor->TimeDateStamp);
}
MakeIdent(nOffset+1);
if (pImportDescriptor->TimeDateStamp) // if bound
printf (TEXT(" Ordinal hint BoundAddrs Name\n"));
else
printf (TEXT(" Ordinal hint Name\n"));
for (pOriginalThunk=pOriginalFirstThunk, pThunk=pFirstThunk; pOriginalThunk->u1.AddressOfData; pOriginalThunk++, pThunk++) {
if (IMAGE_SNAP_BY_ORDINAL32(pOriginalThunk->u1.Ordinal)) {
MakeIdent(nOffset+1);
// Ordinal
if (pImportDescriptor->TimeDateStamp)
printf (TEXT("%4u (0x%04X) 0x%08X\n"),
pOriginalThunk->u1.Ordinal & ~IMAGE_ORDINAL_FLAG32,
pOriginalThunk->u1.Ordinal^IMAGE_ORDINAL_FLAG32,
pThunk->u1.AddressOfData);
else
// pThunk->u1.AddressOfData == pOriginalThunk->u1.Ordinal so don't print it
printf (TEXT("%4u (0x%04X)\n"),
pOriginalThunk->u1.Ordinal & ~IMAGE_ORDINAL_FLAG32,
pOriginalThunk->u1.Ordinal^IMAGE_ORDINAL_FLAG32);
}
else {
IMAGE_IMPORT_BY_NAME *pImportByName = (IMAGE_IMPORT_BY_NAME *) (pOriginalThunk->u1.AddressOfData +
(PBYTE)pbyFile + pSectionHeader->PointerToRawData - pSectionHeader->VirtualAddress);
MakeIdent(nOffset+1);
// Hint - Index into the Export Name Pointer Table. A match is attempted first with this value.
// If it fails, a binary search is performed on the DLL’s Export Name Pointer Table.
if (pImportDescriptor->TimeDateStamp) // if bound
printf (TEXT("%18u (0x%04X) 0x%08X %hs\n"), pImportByName->Hint, pImportByName->Hint, pThunk->u1.AddressOfData,
pImportByName->Name);
else
printf (TEXT("%18u (0x%04X) %hs\n"), pImportByName->Hint, pImportByName->Hint, pImportByName->Name);
}
}
}
if (pFirstBoundImportDescriptor) {
MakeIdent(nOffset);
printf ("PE Header contains the following bound import information:\n");
for (pBoundImportDescriptor=pFirstBoundImportDescriptor; pBoundImportDescriptor->TimeDateStamp;
pBoundImportDescriptor = (IMAGE_BOUND_IMPORT_DESCRIPTOR *)((PBYTE)(pBoundImportDescriptor+1) + pBoundImportDescriptor->NumberOfModuleForwarderRefs*sizeof(IMAGE_BOUND_FORWARDER_REF))) {
PSTR pszDllName = (PSTR) ((DWORD)pFirstBoundImportDescriptor + pBoundImportDescriptor->OffsetModuleName);
IMAGE_BOUND_FORWARDER_REF *pRef = (IMAGE_BOUND_FORWARDER_REF *)(pBoundImportDescriptor+1);
MakeIdent(nOffset+1);
printf ("Bound to %hs", pszDllName);
DumpTimeDateStamp (0, "", pBoundImportDescriptor->TimeDateStamp);
if (pBoundImportDescriptor->NumberOfModuleForwarderRefs) {
UINT i;
for (i=0;i<pBoundImportDescriptor->NumberOfModuleForwarderRefs;i++) {
PSTR pszDllName = (PSTR) ((DWORD)pFirstBoundImportDescriptor + pRef->OffsetModuleName);
MakeIdent(nOffset+2);
printf ("Contained forwarders bound to %hs", pszDllName);
DumpTimeDateStamp (0, "", pRef->TimeDateStamp);
}
}
}
}
}
void DumpExports (UINT nOffset, IMAGE_OPTIONAL_HEADER32 *pOptionalHeader, PBYTE pbyFile,
IMAGE_SECTION_HEADER *pSectionHeader) // header of the section, which contains export section
{
UINT i;
UINT iNames;
PDWORD pdwAddressOfFunctions;
PWORD pwOrdinals;
PDWORD pdwNameRVA;
IMAGE_EXPORT_DIRECTORY *pExportDirectory = (IMAGE_EXPORT_DIRECTORY *)((PBYTE)pbyFile + pSectionHeader->PointerToRawData +
pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress - pSectionHeader->VirtualAddress);
DWORD dwVAExportStart = pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
DWORD dwVAExportEnd = pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress +
pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
DumpDword (nOffset, TEXT("Characteristics"), pExportDirectory->Characteristics);
DumpTimeDateStamp (nOffset, "TimeDateStamp", pExportDirectory->TimeDateStamp);
MakeIdent(nOffset);
printf ("DllName: %s\n", pbyFile + pSectionHeader->PointerToRawData + pExportDirectory->Name - pSectionHeader->VirtualAddress);
DumpDword (nOffset, TEXT("Ordinal Base"), pExportDirectory->Base);
MakeIdent(nOffset);
printf (TEXT("Version: %d.%d\n"), pExportDirectory->MajorVersion, pExportDirectory->MinorVersion);
DumpDword (nOffset, TEXT("Number of exported functions"), pExportDirectory->NumberOfFunctions);
DumpDword (nOffset, TEXT("Number of functions exported by name"), pExportDirectory->NumberOfNames);
MakeIdent(nOffset+1);
printf (TEXT("Ordn hint RVA Name\n"));
pdwAddressOfFunctions = (PDWORD)(pbyFile + pSectionHeader->PointerToRawData + pExportDirectory->AddressOfFunctions - pSectionHeader->VirtualAddress);
pwOrdinals = (PWORD)(pbyFile + pSectionHeader->PointerToRawData + pExportDirectory->AddressOfNameOrdinals - pSectionHeader->VirtualAddress);
pdwNameRVA = (PDWORD)(pbyFile + pSectionHeader->PointerToRawData + pExportDirectory->AddressOfNames - pSectionHeader->VirtualAddress);
for (iNames = 0; iNames < pExportDirectory->NumberOfNames; iNames++) {
MakeIdent(nOffset+1);
// AddressOfFunctions MUST be ouf of Export Directory. If it is not so, it is a Forwarding entry
if (pdwAddressOfFunctions[pwOrdinals[iNames]] < dwVAExportStart ||
pdwAddressOfFunctions[pwOrdinals[iNames]] > dwVAExportEnd)
// AddressOfFunctions is normaly in .text section and export table in .edata or .rdata section, so
// AddressOfFunctions must be not in Export Directory
printf("%4u %4u %08X %s\n",
pwOrdinals[iNames] + pExportDirectory->Base, iNames, pdwAddressOfFunctions[pwOrdinals[iNames]],
(pbyFile + pSectionHeader->PointerToRawData + pdwNameRVA[iNames] - pSectionHeader->VirtualAddress));
else
printf("%4u %4u %s (forwarded to %s)\n",
pwOrdinals[iNames] + pExportDirectory->Base, iNames,
(pbyFile + pSectionHeader->PointerToRawData + pdwNameRVA[iNames] - pSectionHeader->VirtualAddress),
(PSTR)(pbyFile + pSectionHeader->PointerToRawData + pdwAddressOfFunctions[pwOrdinals[iNames]] - pSectionHeader->VirtualAddress));
}
// print functions exported by ordinal
for (i = 0; i < pExportDirectory->NumberOfFunctions; i++) {
if (pdwAddressOfFunctions[i] != 0) {
// if EXPORTS in DEF-file look like
//
// EXPORTS
// Message1 #100
// Message2 #200
// Message3 #300
// Message4 #400
// Message5 #500
// it will be added in export section 401 (500-100+1) entries. 5 from there with not 0 address and the rest
// empty entries with 0
// we will dump only not empty entries
UINT iNames;
WORD wOrdinal = (WORD)(i + pExportDirectory->Base);
// try to find (i + pExportDirectory->Base) ordinal in the list of pwOrdinals
for (iNames = 0; iNames<pExportDirectory->NumberOfNames; iNames++) {
if (pdwAddressOfFunctions[pwOrdinals[iNames]] == pdwAddressOfFunctions[i])
break;
}
if (iNames >= pExportDirectory->NumberOfNames) {
// if not found as exported by name, print it here
MakeIdent(nOffset+1);
if (pdwAddressOfFunctions[i] < pSectionHeader->VirtualAddress ||
pdwAddressOfFunctions[i] > pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize)
printf("%4u %08X [NONAME]\n", wOrdinal, pdwAddressOfFunctions[i]);
else
printf("%4u [NONAME] (forwarded to %s)\n",
wOrdinal, (PSTR)(pbyFile + pSectionHeader->PointerToRawData + pdwAddressOfFunctions[i] - pSectionHeader->VirtualAddress));
}
}
}
}
Related
I have attempted to run this code. Prior to running this, no warnings or errors exist but once it is executed I have an exception thrown and it stops the program from compiling. Here is my code and the error is in the subject line. The CDA file is being used as header file to create a Circular Dynamic Array that is going to be manipulated in Heaps.cpp. The heaps.cpp is to create a binary heap that is to be used in to create Binomial heaps, but that code has not been developed yet.
#include <iostream>
using namespace std;
template <class T>
class CDA
{
private:
int rear;
int size;
int capacity;
T* circArray;
int front;
bool ordered;
T placeHolder;
public:
CDA();
CDA(int s);
~CDA();
int Front();
T Data(int n);
T& operator[](int i);
void AddEnd(T v);
void AddFront(T v);
void DelEnd();
void DelFront();
int Length();
int Capacity();
int Clear();
bool Ordered();
int SetOrdered();
int S01(int n);
T Select(int k);
void InsertionSort();
void QuickSort();
void QuickSort1(int low, int high);
void CountingSort(int m);
int Search(T e);
void reSize();
void Shrink();
int BinarySearch(int left, int right, T e);
int QSortPartition(int low, int high);
void Swap(int* x, int* y);
int QSelPartition(int front, int rear);
T QuickSelect(int front, int rear, int k);
CDA<T>& operator=(const CDA& a);
CDA(const CDA& old);
int Median(int low, int high);
};
template <class T>
CDA<T>::CDA()
{
capacity = 1;
circArray = new T[capacity];
size = 0;
rear = size - 1;
front = -1;
ordered = false;
placeHolder = 0;
}
template <class T>
CDA<T>::CDA(int s)
{
size = s;
capacity = s;
circArray = new T[capacity];
front = 0;
rear = size - 1;
ordered = false;
}
template <class T>
CDA<T>::CDA(const CDA& a)
{
size = a.size;
capacity = a.capacity;
circArray = new T[a.capacity];
front = a.front;
rear = a.size - 1;
ordered = a.ordered;
for (int i = a.front; i < a.front + (a.size); i++)
{
circArray[i % capacity] = a.circArray[i % capacity];
}
}
template <class T>
CDA<T>::~CDA()
{
delete[]circArray;
}
template <class T>
T& CDA<T>::operator[](int i)
{
if (i > capacity)
{
cout << "Array index is out of bounds; exiting." << endl;
placeHolder = i;
cout << endl;
return placeHolder;
exit(0);
}
else
{
return circArray[(front + i) % capacity];
}
}
template <class T>
void CDA<T>::AddEnd(T v)
{
size++;
if (front == -1)
{
circArray[0] = v;
front++;
rear++;
return;
}
if (size > capacity)
{
reSize();
}
else if (front == -1)
{
front = 0;
rear = size - 1;
}
else
{
rear = (rear + 1) % capacity;
}
circArray[rear] = v;
}
template <class T>
void CDA<T>::AddFront(T v)
{
size++;
if (size > capacity)
{
reSize();
}
if (front == -1) //means the array is empty
{
front = 0;
rear = capacity % size;
}
else if (front == 0) //means something is in spot 0
{
front = capacity - 1; //puts front at the end and places the input there
}
else //go until it is back at zero
{
front--;
}
circArray[front] = v;
}
template <class T>
void CDA<T>::DelEnd()
{
size--;
if (size <= capacity / 4)
{
Shrink();
}
else if (rear == front)
{
front = -1;
rear = -1;
}
else
{
rear--;
}
}
template <class T>
void CDA<T>::DelFront()
{
size--;
double shrMeasure;
shrMeasure = capacity / 4.0;
if (size <= shrMeasure) // make an empty and shrink function
{
Shrink();
}
/*
else if (front == rear)
{
if (front == 0)
front = size - 1;
else
front++;
}
*/
else
{
if (front == size) //brings it full circle
{
front = 0;
}
else
{
front++;
}
}
if (front > capacity)
front = front % capacity;
}
template <class T>
int CDA<T>::Length()
{
return size;
}
template <class T>
int CDA<T>::Capacity()
{
return capacity;
}
template <class T>
int CDA<T>::Clear()
{
~CDA();
size = 1;
circArray[size] = NULL;
}
template <class T>
bool CDA<T>::Ordered()
{
return ordered;
}
template <class T>
int CDA<T>::SetOrdered()
{
for (int i = 1; i < size - 1; i++)
{
if (circArray[(i - 1)] > circArray[i])
{
ordered = false;
return -1;
}
}
ordered = true;
return 1;
}
template <class T>
T CDA<T>::Select(int k)
{
if (ordered == true)
{
return circArray[(front + k - 1) % capacity];
}
else
QuickSelect(front, front + (size - 1), k);
}
template <class T>
int CDA<T>::QSelPartition(int left, int right)
{
int pivot = circArray[right % capacity];
int x = left - 1;
//Swap(&circArray[pivIndex], &circArray[right]);
for (int i = left; i <= right - 1; i++)
{
if (circArray[i % capacity] <= pivot)
{
x++;
Swap(&circArray[x % capacity], &circArray[i % capacity]);
}
}
Swap(&circArray[(x + 1) % capacity], &circArray[right % capacity]);
return (x + 1);
}
template <class T>
T CDA<T>::QuickSelect(int left, int right, int k)
{
if (k > 0 && k <= (right - left) + 1)
{
int index = QSelPartition(left, right);
if (index - 1 == k - 1)
return circArray[index % capacity];
else if (index - 1 > k - 1)
return QuickSelect(left, index - 1, k);
else
return QuickSelect(index - 1, right, k - index + left - 1);
}
return -1;
}
template <class T>
void CDA<T>::InsertionSort() //must be utilized in quicksort
{
for (int i = front + 1; i < (front + size); i++)
{
int val = circArray[i % capacity];
int inc = (i - 1) % capacity;
while (inc >= 0 && circArray[inc] > val)
{
circArray[(inc + 1) % capacity] = circArray[inc];
inc--;
if (inc == -1)
{
inc = capacity - 1;
}
}
circArray[(inc + 1) % capacity] = val;
}
ordered = true;
}
template <class T>
void CDA<T>::QuickSort() // change to other quicksort before leaving the ferg
{
QuickSort1(front, front + (size - 1));
}
template <class T>
void CDA<T>::QuickSort1(int low, int high)
{
while (low < high)
{
if (high - low < 900)
{
InsertionSort();
break;
}
else
{
int pivot = QSortPartition(low, high);
if (pivot - low < high - pivot)
{
QuickSort1(low, pivot--);
low = pivot + 1;
}
else
{
QuickSort1(pivot++, high);
high = pivot - 1;
}
}
}
}
template <class T>
int CDA<T>::QSortPartition(int low, int high)
{
int pivot = circArray[Median(low, high) % capacity];
Swap(&circArray[(Median(low, high)) % capacity], &circArray[(high) % capacity]);
int index = low % capacity;
for (int i = low; i < high; i++)
{
if (circArray[i % capacity] <= pivot)
{
T t = circArray[i % capacity];
circArray[i % capacity] = circArray[index % capacity];
circArray[index % capacity] = t;
index++;
}
}
Swap(&circArray[index % capacity], &circArray[high % capacity]);
return index;
}
template <class T>
int CDA<T>::Median(int low, int high)
{
T left, mid, right;
left = circArray[low % capacity];
mid = circArray[((low + high) / 2) % capacity];
right = circArray[high % high];
if (left < right && left > mid)
return low % capacity;
if (left < mid && left > right)
return low % capacity;
if (right < left && right > mid)
return high % capacity;
if (right < mid && right > left)
return high % capacity;
if (mid < left && mid > right)
return ((low + high) / 2 % capacity);
if (mid < right && mid > left)
return ((low + high) / 2 % capacity);
}
template <class T>
void CDA<T>::Swap(int* x, int* y)
{
int temp = *x;
*x = *y;
*y = temp;
}
template <class T>
void CDA<T>::CountingSort(int m) ////NEED TO FIX THIS
{
int* OP = new int[size];
int* Counter = new int[m + 1];
for (int i = front; i <= rear; i++)
{
cout << "CircArray[" << i << "] is " << circArray[i] << endl;
}
for (int i = 0; i <= m; i++)
{
Counter[i] = 0;
}
for (int i = front; i < front + (size); i++)
{
Counter[circArray[i % capacity]]++;
}
for (int i = 1; i <= m; i++)
{
Counter[i] += Counter[i - 1];
}
for (int i = rear - 1; i > 0; i--)
{
OP[Counter[circArray[i]] - 1] = circArray[i];
cout << "Circular array at " << i << " is " << circArray[i] << endl;
Counter[circArray[i]] -= 1;
if (i == front % capacity)
break;
if (i == 0)
i = capacity;
}
for (int i = 0; i < size; i++)
circArray[i] = OP[i];
ordered = true;
front = 0;
}
template <class T>
int CDA<T>::Search(T e)
{
if (ordered == true) //binary search of item e
{
return BinarySearch(front, front + (size - 1), e);
}
else if (ordered == false)
{
for (int i = 0; i < size - 1; i++)
{
if (circArray[i] == e)
return i;
}
}
return -1;
}
template <class T>
int CDA<T>::BinarySearch(int left, int right, T e)
{
while (left <= right)
{
int mid = (left + right) / 2;
int value = circArray[mid % capacity];
if (value == e)
return (mid - front) % capacity;
else if (value < e)
return BinarySearch(mid + 1, right, e);
else if (value > e)
return BinarySearch(left, mid - 1, e);
}
return -1;
}
template <class T>
void CDA<T>::reSize()
{
capacity = capacity * 2;
T *nArray = new T[capacity];
for (int i = 0; i < size - 1; i++)
{
int l = (front + i) % (size-1);
nArray[i] = circArray[l];
}
//delete[]circArray;
circArray = nArray;
front = 0;
rear = (size - 1);
}
template <class T>
void CDA<T>::Shrink()
{
int tFront = front;
capacity = capacity / 2;
T* bArr = new T[capacity];
int index = 0;
while (front <= rear)
{
bArr[index] = circArray[(front + index) % capacity];
index++;
}
T* circArray = bArr;
front = 0;
rear = (size - 1);
}
template <class T>
CDA<T>& CDA<T>::operator=(const CDA<T>& a)
{
if (this != &a)
{
delete[]circArray;
size = a.size;
capacity = a.capacity;
circArray = new T[a.capacity];
front = a.front;
rear = a.size - 1;
ordered = a.ordered;
for (int i = a.front; i < a.front + (a.size); i++)
{
circArray[i % capacity] = a.circArray[i % capacity];
}
}
return *this;
}
template <class T>
int CDA<T>::Front()
{
return front;
}
template <class T>
T CDA<T>::Data(int n)
{
return circArray[n];
}
#include <iostream>
#include "CDA-1.cpp"
using namespace std;
template<class keytype, class valuetype>
class Heap
{
private:
CDA<keytype>* K;
CDA<valuetype>* V;
int size;
public:
Heap()
{
this->K = new CDA<keytype>();
this->V = new CDA<valuetype>();
size = 0;
}
Heap(keytype k[], valuetype v[], int s)
{
//allocate two different arrays for each type
//fill those arrays concurrently using insert
//sort concurrently using heapafy recursively
this->K = new CDA<keytype>(s);
this->V = new CDA<valuetype>(s);
this->size = s;
for (int i = 0; i < s; i++)
{
insert(k[i], v[i]);
}
heapify(s, K->Front());
}
void heapify(int s, int i)
{
//Errors for evans to fix: swap the n's with s.
//Fix the left and right variable logic. Hepaify smallest not small at the bottom.
//Also we need to pass V[] in a parameter so we can edit it in this.
//How to better swap V with K and not just K.
int smallest = i;
int left = 2*i +(-i+1);
int right = 2*i + (-i+2);
keytype kl = K->Data(left);
keytype kr = K->Data(right);
keytype ks = K->Data(smallest);
if (left < s && kl < ks) //FIX
smallest = left;
if (right < s && kr < ks) //FIX
smallest = right;
if (smallest != i)
{
swap(K[i], K[smallest]);
swap(V[i], V[smallest]); //FIX
heapify(s, smallest);
}
}
~Heap() {
return;
}
//items should be inserted using bottom up heap building method
void insert(keytype k, valuetype v)
{
K->AddEnd(k);
V->AddEnd(v);
heapify(size, K->Front());
}
keytype peekKey()
{
int f = K->Front();
return K->Data(f);
}
valuetype peekValue()
{
int f = V->front();
return V->Data(f);
}
keytype extractMin()
{
keytype temp = K->Data(K->Front());
K->DelFront();
V->DelFront();
heapify(size, K->Front());
return temp;
}
void printKey()
{
ActualPrintKey(K->Front());
}
void ActualPrintKey(int n)
{
keytype rt = K->Data(n);
if (rt != size)
{
cout << K->Data(rt) << " ";
ActualPrintKey((2 * n) + (-n + 1));
ActualPrintKey((2 * n) + (-n + 2));
}
}
};
/*
template <class keytype, class valuetype>
class BHeap
{
BHeap();
BHeap(keytype k[], valuetype v[], int s);
~BHeap();
keytype peekKey();
valuetype peekValue();
keytype extractMin();
//items should be inserted using repeated insertion
void insert(keytype k, valuetype v);
void merge(BHeap<keytype, valuetype>& H2);
void printKey();
};
*/
#include <iostream>
#include "Heaps.cpp"
using namespace std;
int main() {
string K[10] = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "K" };
int V[10] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
Heap<string, int> T1, T2(K, V, 10);
cout << T2.peekKey() << endl;
cout << endl;
system("pause");
return 0;
}
In general "Access violation reading location", means you are trying to read virtually memory address space to a process which does not belong to your application and the operating system protective mechanism is kicking in to protect the rest of the loaded applications and resource from been accessed (read or write) by your application "memory leak vulnerability". If I was at your place I would review the code and all variables and arrays if they are properly initialized before use. Another thing which needs to be taken in consideration is the operating system and the permissions required by your application (Windows run as Administrator / GNU/Linux sudo).
Cheers
My code involves both Processing and Arduino. 5 different photocells are triggering 5 different sounds. My sound files play only when the ldrvalue is above the threshold.
The Null Pointer Exception is highlighted on this line
for (int i = 0; i < ldrValues.length; i++) {
I am not sure which part of my code should be changed so that I can run it.
import processing.serial.*;
import processing.sound.*;
SoundFile[] soundFiles = new SoundFile[5];
Serial myPort; // Create object from Serial class
int[] ldrValues;
int[] thresholds = {440, 490, 330, 260, 450};
int i = 0;
boolean[] states = {false, false, false, false, false};
void setup() {
size(200, 200);
println((Object[])Serial.list());
String portName = Serial.list()[3];
myPort = new Serial(this, portName, 9600);
soundFiles[0] = new SoundFile(this, "1.mp3");
soundFiles[1] = new SoundFile(this, "2.mp3");
soundFiles[2] = new SoundFile(this, "3.mp3");
soundFiles[3] = new SoundFile(this, "4.mp3");
soundFiles[4] = new SoundFile(this, "5.mp3");
}
void draw()
{
background(255);
//serial loop
while (myPort.available() > 0) {
String myString = myPort.readStringUntil(10);
if (myString != null) {
//println(myString);
ldrValues = int(split(myString.trim(), ','));
//println(ldrValues);
}
}
for (int i = 0; i < ldrValues.length; i++) {
println(states[i]);
println(ldrValues[i]);
if (ldrValues[i] > thresholds[i] && !states[i]) {
println("sensor " + i + " is activated");
soundFiles[i].play();
states[i] = true;
}
if (ldrValues[i] < thresholds[i]) {
println("sensor " + i + " is NOT activated");
soundFiles[i].stop();
states[i] = false;
}
}
}
You're approach is shall we say optimistic ? :)
It's always assuming there was a message from Serial, always formatted the right way so it could be parsed and there were absolutely 0 issues buffering data (incomplete strings, etc.))
The simplest thing you could do is check if the parsing was successful, otherwise the ldrValues array would still be null:
void draw()
{
background(255);
//serial loop
while (myPort.available() > 0) {
String myString = myPort.readStringUntil(10);
if (myString != null) {
//println(myString);
ldrValues = int(split(myString.trim(), ','));
//println(ldrValues);
}
}
// double check parsing int values from the string was successfully as well, not just buffering the string
if(ldrValues != null){
for (int i = 0; i < ldrValues.length; i++) {
println(states[i]);
println(ldrValues[i]);
if (ldrValues[i] > thresholds[i] && !states[i]) {
println("sensor " + i + " is activated");
soundFiles[i].play();
states[i] = true;
}
if (ldrValues[i] < thresholds[i]) {
println("sensor " + i + " is NOT activated");
soundFiles[i].stop();
states[i] = false;
}
}
}else{
// print a helpful debugging message otherwise
println("error parsing ldrValues from string: " + myString);
}
}
(Didn't know you could parse a int[] with int(): nice!)
I'm trying to read an intel .hex file using the following VHDL code snippet. My synthesizer is having a problem with the part of the code that is supposed to check for and discard the ':' character at the start of a line. The synthesis tool gives this error "Call to procedure without body" (line marked with comment). I have never seen this error and don't know what it means. Is there a solution for this error (or an alternate way to discard the ':' character)?
function Load_Data(constant x: in integer) return ROM_Data is
use std.textio.all;
use ieee.std_logic_textio.all;
file ROMFILE: TEXT open READ_MODE is "IIU_Code.hex";
variable newline: line;
variable newchar: character;
variable newbyte: std_logic_vector(7 downto 0);
variable newword: std_logic_vector(15 downto 0);
variable NextAddr, ByteCount: integer;
variable NewROM: ROM_Data := (others => (others => '0'));
variable valid: boolean := True;
begin
while (valid) loop
readline(ROMFILE, newline);
read(newline,newchar,valid); --ERROR HERE!!!
if (newchar = ':') and (valid = True) then
hread(newline,newbyte);
ByteCount := to_integer(unsigned(newbyte));
hread(newline,newword);
NextAddr := to_integer(unsigned(newword));
hread(newline,newbyte);
if newbyte = X"01" then --check for EOF marker
valid := False;
end if;
for i in 1 to ByteCount loop
hread(newline,newbyte);
NewROM(NextAddr) := newbyte;
NextAddr := NextAddr + 1;
end loop;
end if;
end loop;
file_close(ROMFILE);
return NewROM;
end;
In lieu of trying to force synthesis to initialize ROM from a file I've been known to write C programs that convert data for models to constants, in this case by generating entity/architecture pairs:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAX_VECTOR 512
void rom_header (rom_name,array_size)
char *rom_name;
int array_size;
{
printf("library ieee;\nuse ieee.std_logic_1164.all;\n");
printf("\nentity %s is\n port (\n",rom_name);
printf("\tindex:\t\tin integer range 0 to %d;\n",array_size*8-1);
printf("\tOE:\t\tin std_logic;\n");
printf("\toutput:\t\tout std_logic_vector (7 downto 0)\n");
printf(" );\nend ;\n");
printf("\narchitecture behave of %s is\n\n",rom_name);
printf(" subtype bytestring is bit_vector( 7 downto 0);\n");
printf(" type bytestream is array (0 to %d) of bytestring;\n\n",
array_size*8-1);
printf(" constant byte_array:\tbytestream := (\n\t ");
}
void rom_tail() {
printf(" begin\n\n");
printf(" output <= To_StdLogicVector(byte_array(index)) ");
printf("when OE = '1' else\n");
printf(" (others => 'Z') ");
printf("when OE = '0' else\n");
printf(" (others => 'X');\n");
printf("\n\nend behave;\n\n");
}
int main (argc,argv)
int argc;
char *argv[];
{
extern char *optarg;
extern int optind, opterr;
extern int getopt();
char *infile;
char key_vector[MAX_VECTOR][16];
char plain_vector[MAX_VECTOR][16];
char cipher_vector[MAX_VECTOR][16];
char testinput[2047];
char testkey[17];
char testplain[17];
char testcipher[17];
int encrypt[MAX_VECTOR];
int i;
int len;
int testcount = 0;
int totalcount = 0;
int linenumber = 0;
int vector = 0;
int encode = 1;
while ( (i=getopt(argc,argv,"i:")) != -1 ) {
switch (i) {
case 'i':
infile = optarg;
if((freopen(optarg,"r",stdin)) == NULL) {
fprintf(stderr,"ERROR:%s, can't open %s for input\n",
argv[0],optarg);
exit(-1);
}
break;
case '?':
fprintf(stderr,"usage: %s [-i infile] \n",argv[0]);
fprintf(stderr,"\ngenerates VHDL arrays for DES test vectors:\n");
fprintf(stderr,"\tcipher_vector.vhdl\n");
fprintf(stderr,"\tencrypt_vector.vhdl\n");
fprintf(stderr,"\tkey_vector.vhdl\n");
fprintf(stderr,"\tplain_vector.vhdl\n");
exit (-1);
break;
}
}
while (fgets(testinput,(sizeof testinput) -1, stdin) != NULL ) {
linenumber++;
if ( strncmp(testinput,"encrypt",7) == 0) { /* mode = encode */
encode = 1;
fprintf(stderr,"%s",testinput);
}
else
if ( strncmp(testinput,"decrypt",7) == 0) { /* mode = decode */
fprintf(stderr,"%s",testinput);
encode = 0;
}
else
if ( strncmp(testinput," ",1) == 0) { /* key, plain & cipher */
testcount++;
len = sscanf(testinput,"%s%s%s*", testkey, testplain, testcipher);
if (len != 3) {
fprintf(stderr,"ERROR: %s, wrong vector count, line %d\n",
argv[0], linenumber);
exit(-1);
}
else if (strlen(testkey) != 16) {
fprintf(stderr,"ERROR: %s wrong byte count testkey, line %d\n",
argv[0],linenumber);
exit(-1);
}
else if (strlen(testplain) != 16) {
fprintf(stderr,"ERROR: %s wrong byte count testplain, line %d\n",
argv[0],linenumber);
exit(-1);
}
else if (strlen(testcipher) != 16) {
fprintf(stderr,"ERROR: %s wrong byte count testcipher, line %d\n",
argv[0],linenumber);
exit(-1);
}
else {
encrypt[vector] = encode;
strncpy( key_vector[vector], testkey,16);
strncpy( plain_vector[vector], testplain,16);
strncpy(cipher_vector[vector],testcipher,16);
for ( i = 0; i < 16; i++) {
if ( !isxdigit(key_vector[vector][i]) ||
!isxdigit(plain_vector[vector][i]) ||
!isxdigit(cipher_vector[vector][i]) ) {
fprintf(stderr,"ERROR: %s, Vector: %d contains nonhex\n",
argv[0], vector+1);
fprintf(stderr,"\t%s\n",testinput);
exit(-1);
}
}
}
vector++;
if (vector == MAX_VECTOR) {
fprintf(stderr,"%s: Maximum number of vectors = %d\n",
argv[0],MAX_VECTOR);
exit(0);
}
}
else { /* nothing but eyewash */
if ( testcount ) {
fprintf(stderr," %d test vectors\n",testcount);
totalcount +=testcount;
testcount = 0;
}
}
}
fprintf(stderr," Total: %d test vectors\n",totalcount);
if (freopen("key_vector.vhdl","w",stdout) == NULL){
fprintf(stderr,"ERROR: %s can write to key_vector.vhdl\n",argv[0]);
exit (-1);
}
rom_header("key_vector",totalcount);
for(vector = 0; vector < totalcount; vector++) {
for ( i = 0; i <= 15; i++) {
if ( !(i & 1)) {
printf("x\"%c",key_vector[vector][i]);
}
else {
if ( i < 15) {
printf("%c\",",key_vector[vector][i]);
}
else {
printf("%c\"",key_vector[vector][i]); // no comma
}
}
}
if (vector != totalcount-1)
printf(",\n\t ");
else
printf("\n\t);\n");
}
rom_tail();
if (freopen("plain_vector.vhdl","w",stdout) == NULL){
fprintf(stderr,"ERROR: %s can write to plain_vector.vhdl\n",argv[0]);
exit (-1);
}
rom_header("plain_vector",totalcount);
for(vector = 0; vector < totalcount; vector++) {
for ( i = 0; i <= 15; i++) {
if ( !(i & 1)) {
printf("x\"%c",plain_vector[vector][i]);
}
else {
if ( i < 15) {
printf("%c\",",plain_vector[vector][i]);
}
else {
printf("%c\"",plain_vector[vector][i]); // no comma
}
}
}
if (vector != totalcount-1)
printf(",\n\t ");
else
printf("\n\t);\n");
}
rom_tail();
if (freopen("cipher_vector.vhdl","w",stdout) == NULL){
fprintf(stderr,"ERROR: %s can write to cipher_vector.vhdl\n",argv[0]);
exit (-1);
}
rom_header("cipher_vector",totalcount);
for(vector = 0; vector < totalcount; vector++) {
for ( i = 0; i <= 15; i++) {
if ( !(i & 1)) {
printf("x\"%c",cipher_vector[vector][i]);
}
else {
if ( i < 15) {
printf("%c\",",cipher_vector[vector][i]);
}
else {
printf("%c\"",cipher_vector[vector][i]); // no comma
}
}
}
if (vector != totalcount-1)
printf(",\n\t ");
else
printf("\n\t);\n");
}
rom_tail();
if (freopen("encrypt_vector.vhdl","w",stdout) == NULL){
fprintf(stderr,"ERROR: %s can write to encrypt_vector.vhdl\n",argv[0]);
exit (-1);
}
printf("library ieee;\nuse ieee.std_logic_1164.all;\n");
printf("\nentity encrypt_vector is\n port (\n");
printf("\tindex:\t\tin integer range 0 to %d;\n",totalcount-1);
printf("\toutput:\t\tout std_logic\n");
printf(" );\nend ;\n");
printf("\narchitecture behave of encrypt_vector is\n\n");
printf(" constant bit_array:\tstd_logic_vector(0 to %d) := (\n\t ",
totalcount-1);
i = 0;
for(vector = 0; vector < totalcount; vector++) {
printf("'%1d'",encrypt[vector]);i++;
if ((i == 16) && (vector != totalcount-1)) {
printf(",\n\t ");
i = 0;
}
else if (vector == totalcount-1)
printf("\n\t);\n");
else
printf(",");
}
printf(" begin\n\n");
printf(" output <= bit_array(index);");
printf("\n\nend behave;\n\n");
exit (0);
}
You could also do this for packages or even subprograms.
This particular conversion software uses a form of valid vectors preceded by an encryption mode switch and having a first column space, providing hex values of the right string length:
#
encrypt
#
0101010101010101 95F8A5E5DD31D900 8000000000000000
0101010101010101 DD7F121CA5015619 4000000000000000
0101010101010101 2E8653104F3834EA 2000000000000000
0101010101010101 4BD388FF6CD81D4F 1000000000000000
0101010101010101 20B9E767B2FB1456 0800000000000000
0101010101010101 55579380D77138EF 0400000000000000
0101010101010101 6CC5DEFAAF04512F 0200000000000000
#
It's the test vectors for a byte wide interfaced DES chip, and in this case only used in a test bench. There's nothing stopping you from embedding something like you want.
This little C program is quite old but I believe I updated it recently enough it would compile and run, it spits out several different 'vector' files for the test bench based on what the values are used for. It wants the input file to be concluded with a comment line ('#' in the first column), followed by a newline.
So the message here is don't count directly on your synthesis tools to initialize data (unless they handle it with explicitly supported routines).
See How to synthesis a rom and load initial data into it ?, for a hint thread in Xilinx, otherwise noting you haven't specified target platform.
addendum
The questioner has been forthcoming with additional information in comments, wherein automated software has exhorted us to Please avoid extended discussions in comments.
The target is a Microsemi ProASIC3, which also prompted another look at the provided Load_Data function, whose input argument x doesn't show up in the function body. While that indicates the author may have been battling uphill restrictions trying to read a file.
Looking at Microsemi's web site we see that a ProASIC3 can have an embedded 1K bit FLASHROM, which may or may not be the ROM in question. I'm an ASIC designer from way back and can appreciate the size range of these devices, intended for among other uses System on Chip applications. You'd expect the vendor would be able to supply information on how to use the FLASHROM.
For other ROM purposes in lieu of vendor supplied method of loading ROM it would seem that creating a synthesis compatible method of embedding an array of constants is in order (analogous to what's shown in the C programming example).
One characteristic of Read Only Memory in programmable devices is that the values are typically included as part of device programming.
I am trying to set up a program that when you type /s then a movie title (ex. /s Green Mile it searches for that movie title in the movies array but my strstr function is not working. Also /a adds movie /r shows all movies in database and /q quits program.
#include <stdio.h>
#include <string.h>
main() {
char movies[1000][64];
int i;
int j = 1;
int k = 0;
int counter = 0;
char buffer[64];
char buffer2[64];
int len;
do {
printf("Enter command for movie database:\n");
fgets(buffer, 64, stdin);
if (buffer[1] == 'a') {
len = strlen(buffer);
if (buffer[len - 1] == '\n')
buffer[len - 1] = '\0';
for(i=3; i<sizeof(buffer); i++) {
movies[counter][k] = buffer[i];
k++;
}
printf("You have chosen to enter a movie in the database.\n");
printf("The movie added to the database is: %s\n", &movies[counter][0]);
counter++;
k = 0;
}
if (buffer[1] == 'q') {
printf("You have chosen to quit.\n");
printf("Goodbye.\n");
}
if (buffer[1] == 's') {
for(i=2; i<sizeof(buffer); i++) {
buffer2[k] = buffer[i];
k++;
}
for (i = 0; i < counter; i++) {
if (strstr(movies[i], buffer2) != NULL) {
printf("Found %s in position %d,%s\n", buffer2, i + 1, movies[i]);
printf("Index of Movie is %d.\n", i + 1);
}
}
k = 0;
}
if (buffer[1] == 'r') {
printf("You have choosen to report movies in database\n");
printf("These are the movies you have watched:\n");
for( i = 0; i < counter; i++ ) {
printf("%d: %s\n", j, &movies[i][0]);
j++;
}
printf("\n");
j = 1;
}
} while (buffer[1] != 'q');
}
Revised code.
#include <stdio.h>
#include <string.h>
main() {
char movies[1000][64];
int counter = 0;
char buffer[64];
char * movieTitle = &buffer[3];
while(true) {
printf("Enter command for movie database:\n");
fgets(buffer, 64, stdin);
//replace terminator every time
int len = strlen(buffer);
if (buffer[len - 1] == '\n')
buffer[len - 1] = '\0';
if(strstr(buffer, "/a") && counter < 1000) {
strcpy(movies[counter], movieTitle);
printf("You have chosen to enter a movie in the database.\n");
printf("The movie added to the database is: %s\n", &movies[counter][0]);
counter++;
}
else if (strstr(buffer, "/s")) {
for (int i = 0; i < counter; i++) {
if (strcmp(movies[i], movieTitle) == 0) {
printf("Found %s in position %d,%s\n", movieTitle, i + 1, movies[i]);
printf("Index of Movie is %d.\n", i + 1);
}
}
}
else if (strstr(buffer, "/r")) {
printf("You have choosen to report movies in database\n");
printf("These are the movies you have watched:\n");
for( i = 0; i < counter; i++ ) {
printf("%d: %s\n", i + 1, &movies[i][0]);
}
printf("\n");
}
else if (strstr(buffer, "/q")) {
printf("You have chosen to quit.\n");
printf("Goodbye.\n");
break;
}
else {
printf("unrecognized command");
}
}
}
TestNG generates an emailable report. I have seen that this report can be customized by using Listeners. But could not get what i wanted. My requirement is to include extra details in the summary section of this report. I want to be able to add may be a new table or extra columns to show the environment details of the test execution.
Trying to attach a screenshot but apparently missing something and it does not come up.
That is what I have on my framework. I'll try to explain it (sorry my English)
Copy ReporterListenerAdapter.java and rename as MyReporterListenerAdapter.java, put it on your java project (/listener folder for example)
public class MyReporterListenerAdapter implements IReporter {
public void generateReport(List<XmlSuite> xml, List<ISuite> suites, String outdir) {}
}
Next, copy ReporterListener.java and rename as MyReporterListener.java
Too much code to paste here, but on createWriter function change the report name. For example: "emailable-MyFramework-report".
MyReporterListener.java
public class MyReporterListener extends MyReporterListenerAdapter {
private static final Logger L = Logger.getLogger(MyReporterListener.class);
// ~ Instance fields ------------------------------------------------------
private PrintWriter m_out;
private int m_row;
private Integer m_testIndex;
private int m_methodIndex;
private Scanner scanner;
// ~ Methods --------------------------------------------------------------
/** Creates summary of the run */
#Override
public void generateReport(List<XmlSuite> xml, List<ISuite> suites,
String outdir) {
try {
m_out = createWriter(outdir);
} catch (IOException e) {
L.error("output file", e);
return;
}
startHtml(m_out);
generateSuiteSummaryReport(suites);
generateMethodSummaryReport(suites);
generateMethodDetailReport(suites);
endHtml(m_out);
m_out.flush();
m_out.close();
}
protected PrintWriter createWriter(String outdir) throws IOException {
java.util.Date now = new Date();
new File(outdir).mkdirs();
return new PrintWriter(new BufferedWriter(new FileWriter(new File(
outdir, "emailable-FON-report"
+ DateFunctions.dateToDayAndTimeForFileName(now)
+ ".html"))));
}
/**
* Creates a table showing the highlights of each test method with links to
* the method details
*/
protected void generateMethodSummaryReport(List<ISuite> suites) {
m_methodIndex = 0;
startResultSummaryTable("methodOverview");
int testIndex = 1;
for (ISuite suite : suites) {
if (suites.size() > 1) {
titleRow(suite.getName(), 5);
}
Map<String, ISuiteResult> r = suite.getResults();
for (ISuiteResult r2 : r.values()) {
ITestContext testContext = r2.getTestContext();
String testName = testContext.getName();
m_testIndex = testIndex;
resultSummary(suite, testContext.getFailedConfigurations(),
testName, "failed", " (configuration methods)");
resultSummary(suite, testContext.getFailedTests(), testName,
"failed", "");
resultSummary(suite, testContext.getSkippedConfigurations(),
testName, "skipped", " (configuration methods)");
resultSummary(suite, testContext.getSkippedTests(), testName,
"skipped", "");
resultSummary(suite, testContext.getPassedTests(), testName,
"passed", "");
testIndex++;
}
}
m_out.println("</table>");
}
/** Creates a section showing known results for each method */
protected void generateMethodDetailReport(List<ISuite> suites) {
m_methodIndex = 0;
for (ISuite suite : suites) {
Map<String, ISuiteResult> r = suite.getResults();
for (ISuiteResult r2 : r.values()) {
ITestContext testContext = r2.getTestContext();
if (r.values().size() > 0) {
m_out.println("<h1>" + testContext.getName() + "</h1>");
}
resultDetail(testContext.getFailedConfigurations());
resultDetail(testContext.getFailedTests());
resultDetail(testContext.getSkippedConfigurations());
resultDetail(testContext.getSkippedTests());
resultDetail(testContext.getPassedTests());
}
}
}
/**
* #param tests
*/
private void resultSummary(ISuite suite, IResultMap tests, String testname,
String style, String details) {
if (tests.getAllResults().size() > 0) {
StringBuffer buff = new StringBuffer();
String lastClassName = "";
int mq = 0;
int cq = 0;
for (ITestNGMethod method : getMethodSet(tests, suite)) {
m_row += 1;
m_methodIndex += 1;
ITestClass testClass = method.getTestClass();
String className = testClass.getName();
if (mq == 0) {
String id = (m_testIndex == null ? null : "t"
+ Integer.toString(m_testIndex));
titleRow(testname + " — " + style + details, 5, id);
m_testIndex = null;
}
if (!className.equalsIgnoreCase(lastClassName)) {
if (mq > 0) {
cq += 1;
m_out.print("<tr class=\"" + style
+ (cq % 2 == 0 ? "even" : "odd") + "\">"
+ "<td");
if (mq > 1) {
m_out.print(" rowspan=\"" + mq + "\"");
}
m_out.println(">" + lastClassName + "</td>" + buff);
}
mq = 0;
buff.setLength(0);
lastClassName = className;
}
Set<ITestResult> resultSet = tests.getResults(method);
long end = Long.MIN_VALUE;
long start = Long.MAX_VALUE;
for (ITestResult testResult : tests.getResults(method)) {
if (testResult.getEndMillis() > end) {
end = testResult.getEndMillis();
}
if (testResult.getStartMillis() < start) {
start = testResult.getStartMillis();
}
}
mq += 1;
if (mq > 1) {
buff.append("<tr class=\"" + style
+ (cq % 2 == 0 ? "odd" : "even") + "\">");
}
String description = method.getDescription();
String testInstanceName = resultSet
.toArray(new ITestResult[] {})[0].getTestName();
buff.append("<td><a href=\"#m"
+ m_methodIndex
+ "\">"
+ qualifiedName(method)
+ " "
+ (description != null && description.length() > 0 ? "(\""
+ description + "\")"
: "")
+ "</a>"
+ (null == testInstanceName ? "" : "<br>("
+ testInstanceName + ")") + "</td>"
+ "<td class=\"numi\">" + resultSet.size() + "</td>"
+ "<td>" + start + "</td>" + "<td class=\"numi\">"
+ (end - start) + "</td>" + "</tr>");
}
if (mq > 0) {
cq += 1;
m_out.print("<tr class=\"" + style
+ (cq % 2 == 0 ? "even" : "odd") + "\">" + "<td");
if (mq > 1) {
m_out.print(" rowspan=\"" + mq + "\"");
}
m_out.println(">" + lastClassName + "</td>" + buff);
}
}
}
/** Starts and defines columns result summary table */
private void startResultSummaryTable(String style) {
tableStart(style, "summary");
m_out.println("<tr><th>Class</th>"
+ "<th>Method</th><th># of<br/>Scenarios</th><th>Start</th><th>Time<br/>(ms)</th></tr>");
m_row = 0;
}
private String qualifiedName(ITestNGMethod method) {
StringBuilder addon = new StringBuilder();
String[] groups = method.getGroups();
int length = groups.length;
if (length > 0 && !"basic".equalsIgnoreCase(groups[0])) {
addon.append("(");
for (int i = 0; i < length; i++) {
if (i > 0) {
addon.append(", ");
}
addon.append(groups[i]);
}
addon.append(")");
}
return "<b>" + method.getMethodName() + "</b> " + addon;
}
private void resultDetail(IResultMap tests) {
for (ITestResult result : tests.getAllResults()) {
ITestNGMethod method = result.getMethod();
m_methodIndex++;
String cname = method.getTestClass().getName();
m_out.println("<h2 id=\"m" + m_methodIndex + "\">" + cname + ":"
+ method.getMethodName() + "</h2>");
Set<ITestResult> resultSet = tests.getResults(method);
generateForResult(result, method, resultSet.size());
m_out.println("<p class=\"totop\">back to summary</p>");
}
}
/**
* Write the first line of the stack trace
*
* #param tests
*/
private void getShortException(IResultMap tests) {
for (ITestResult result : tests.getAllResults()) {
m_methodIndex++;
Throwable exception = result.getThrowable();
List<String> msgs = Reporter.getOutput(result);
boolean hasReporterOutput = msgs.size() > 0;
boolean hasThrowable = exception != null;
if (hasThrowable) {
boolean wantsMinimalOutput = result.getStatus() == ITestResult.SUCCESS;
if (hasReporterOutput) {
m_out.print("<h3>"
+ (wantsMinimalOutput ? "Expected Exception"
: "Failure") + "</h3>");
}
// Getting first line of the stack trace
String str = Utils.stackTrace(exception, true)[0];
scanner = new Scanner(str);
String firstLine = scanner.nextLine();
m_out.println(firstLine);
}
}
}
/**
* Write all parameters
*
* #param tests
*/
private void getParameters(IResultMap tests) {
for (ITestResult result : tests.getAllResults()) {
m_methodIndex++;
Object[] parameters = result.getParameters();
boolean hasParameters = parameters != null && parameters.length > 0;
if (hasParameters) {
for (Object p : parameters) {
m_out.println(Utils.escapeHtml(Utils.toString(p)) + " | ");
}
}
}
}
private void generateForResult(ITestResult ans, ITestNGMethod method,
int resultSetSize) {
Object[] parameters = ans.getParameters();
boolean hasParameters = parameters != null && parameters.length > 0;
if (hasParameters) {
tableStart("result", null);
m_out.print("<tr class=\"param\">");
for (int x = 1; x <= parameters.length; x++) {
m_out.print("<th>Param." + x + "</th>");
}
m_out.println("</tr>");
m_out.print("<tr class=\"param stripe\">");
for (Object p : parameters) {
m_out.println("<td>" + Utils.escapeHtml(Utils.toString(p))
+ "</td>");
}
m_out.println("</tr>");
}
List<String> msgs = Reporter.getOutput(ans);
boolean hasReporterOutput = msgs.size() > 0;
Throwable exception = ans.getThrowable();
boolean hasThrowable = exception != null;
if (hasReporterOutput || hasThrowable) {
if (hasParameters) {
m_out.print("<tr><td");
if (parameters.length > 1) {
m_out.print(" colspan=\"" + parameters.length + "\"");
}
m_out.println(">");
} else {
m_out.println("<div>");
}
if (hasReporterOutput) {
if (hasThrowable) {
m_out.println("<h3>Test Messages</h3>");
}
for (String line : msgs) {
m_out.println(line + "<br/>");
}
}
if (hasThrowable) {
boolean wantsMinimalOutput = ans.getStatus() == ITestResult.SUCCESS;
if (hasReporterOutput) {
m_out.println("<h3>"
+ (wantsMinimalOutput ? "Expected Exception"
: "Failure") + "</h3>");
}
generateExceptionReport(exception, method);
}
if (hasParameters) {
m_out.println("</td></tr>");
} else {
m_out.println("</div>");
}
}
if (hasParameters) {
m_out.println("</table>");
}
}
protected void generateExceptionReport(Throwable exception,
ITestNGMethod method) {
m_out.print("<div class=\"stacktrace\">");
m_out.print(Utils.stackTrace(exception, true)[0]);
m_out.println("</div>");
}
/**
* Since the methods will be sorted chronologically, we want to return the
* ITestNGMethod from the invoked methods.
*/
private Collection<ITestNGMethod> getMethodSet(IResultMap tests,
ISuite suite) {
List<IInvokedMethod> r = Lists.newArrayList();
List<IInvokedMethod> invokedMethods = suite.getAllInvokedMethods();
for (IInvokedMethod im : invokedMethods) {
if (tests.getAllMethods().contains(im.getTestMethod())) {
r.add(im);
}
}
Arrays.sort(r.toArray(new IInvokedMethod[r.size()]), new TestSorter());
List<ITestNGMethod> result = Lists.newArrayList();
// Add all the invoked methods
for (IInvokedMethod m : r) {
result.add(m.getTestMethod());
}
// Add all the methods that weren't invoked (e.g. skipped) that we
// haven't added yet
for (ITestNGMethod m : tests.getAllMethods()) {
if (!result.contains(m)) {
result.add(m);
}
}
return result;
}
#SuppressWarnings("unused")
public void generateSuiteSummaryReport(List<ISuite> suites) {
tableStart("testOverview", null);
m_out.print("<tr>");
tableColumnStart("Test");
tableColumnStart("Methods<br/>Passed");
tableColumnStart("Scenarios<br/>Passed");
tableColumnStart("# skipped");
tableColumnStart("# failed");
tableColumnStart("Error messages");
tableColumnStart("Parameters");
tableColumnStart("Start<br/>Time");
tableColumnStart("End<br/>Time");
tableColumnStart("Total<br/>Time");
tableColumnStart("Included<br/>Groups");
tableColumnStart("Excluded<br/>Groups");
m_out.println("</tr>");
NumberFormat formatter = new DecimalFormat("#,##0.0");
int qty_tests = 0;
int qty_pass_m = 0;
int qty_pass_s = 0;
int qty_skip = 0;
int qty_fail = 0;
long time_start = Long.MAX_VALUE;
long time_end = Long.MIN_VALUE;
m_testIndex = 1;
for (ISuite suite : suites) {
if (suites.size() > 1) {
titleRow(suite.getName(), 8);
}
Map<String, ISuiteResult> tests = suite.getResults();
for (ISuiteResult r : tests.values()) {
qty_tests += 1;
ITestContext overview = r.getTestContext();
startSummaryRow(overview.getName());
int q = getMethodSet(overview.getPassedTests(), suite).size();
qty_pass_m += q;
summaryCell(q, Integer.MAX_VALUE);
q = overview.getPassedTests().size();
qty_pass_s += q;
summaryCell(q, Integer.MAX_VALUE);
q = getMethodSet(overview.getSkippedTests(), suite).size();
qty_skip += q;
summaryCell(q, 0);
q = getMethodSet(overview.getFailedTests(), suite).size();
qty_fail += q;
summaryCell(q, 0);
// NEW
// Insert error found
m_out.print("<td class=\"numi" + (true ? "" : "_attn") + "\">");
getShortException(overview.getFailedTests());
getShortException(overview.getSkippedTests());
m_out.println("</td>");
// NEW
// Add parameters for each test case (failed or passed)
m_out.print("<td class=\"numi" + (true ? "" : "_attn") + "\">");
// Write OS and Browser
// m_out.println(suite.getParameter("os").substring(0, 3) +
// " | "
// + suite.getParameter("browser").substring(0, 3) + " | ");
getParameters(overview.getFailedTests());
getParameters(overview.getPassedTests());
getParameters(overview.getSkippedTests());
m_out.println("</td>");
// NEW
summaryCell(
DateFunctions.dateToDayAndTime(overview.getStartDate()),
true);
m_out.println("</td>");
summaryCell(
DateFunctions.dateToDayAndTime(overview.getEndDate()),
true);
m_out.println("</td>");
time_start = Math.min(overview.getStartDate().getTime(),
time_start);
time_end = Math.max(overview.getEndDate().getTime(), time_end);
summaryCell(
formatter.format((overview.getEndDate().getTime() - overview
.getStartDate().getTime()) / 1000.)
+ " seconds", true);
summaryCell(overview.getIncludedGroups());
summaryCell(overview.getExcludedGroups());
m_out.println("</tr>");
m_testIndex++;
}
}
if (qty_tests > 1) {
m_out.println("<tr class=\"total\"><td>Total</td>");
summaryCell(qty_pass_m, Integer.MAX_VALUE);
summaryCell(qty_pass_s, Integer.MAX_VALUE);
summaryCell(qty_skip, 0);
summaryCell(qty_fail, 0);
summaryCell(" ", true);
summaryCell(" ", true);
summaryCell(" ", true);
summaryCell(" ", true);
summaryCell(
formatter.format(((time_end - time_start) / 1000.) / 60.)
+ " minutes", true);
m_out.println("<td colspan=\"3\"> </td></tr>");
}
m_out.println("</table>");
}
private void summaryCell(String[] val) {
StringBuffer b = new StringBuffer();
for (String v : val) {
b.append(v + " ");
}
summaryCell(b.toString(), true);
}
private void summaryCell(String v, boolean isgood) {
m_out.print("<td class=\"numi" + (isgood ? "" : "_attn") + "\">" + v
+ "</td>");
}
private void startSummaryRow(String label) {
m_row += 1;
m_out.print("<tr"
+ (m_row % 2 == 0 ? " class=\"stripe\"" : "")
+ "><td style=\"text-align:left;padding-right:2em\"><a href=\"#t"
+ m_testIndex + "\">" + label + "</a>" + "</td>");
}
private void summaryCell(int v, int maxexpected) {
summaryCell(String.valueOf(v), v <= maxexpected);
}
private void tableStart(String cssclass, String id) {
m_out.println("<table cellspacing=\"0\" cellpadding=\"0\""
+ (cssclass != null ? " class=\"" + cssclass + "\""
: " style=\"padding-bottom:2em\"")
+ (id != null ? " id=\"" + id + "\"" : "") + ">");
m_row = 0;
}
private void tableColumnStart(String label) {
m_out.print("<th>" + label + "</th>");
}
private void titleRow(String label, int cq) {
titleRow(label, cq, null);
}
private void titleRow(String label, int cq, String id) {
m_out.print("<tr");
if (id != null) {
m_out.print(" id=\"" + id + "\"");
}
m_out.println("><th colspan=\"" + cq + "\">" + label + "</th></tr>");
m_row = 0;
}
/** Starts HTML stream */
protected void startHtml(PrintWriter out) {
out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">");
out.println("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
out.println("<head>");
out.println("<title>Hector Flores - TestNG Report</title>");
out.println("<style type=\"text/css\">");
out.println("table {margin-bottom:10px;border-collapse:collapse;empty-cells:show}");
out.println("td,th {border:1px solid #009;padding:.25em .5em}");
out.println(".result th {vertical-align:bottom}");
out.println(".param th {padding-left:1em;padding-right:1em}");
out.println(".param td {padding-left:.5em;padding-right:2em}");
out.println(".stripe td,.stripe th {background-color: #E6EBF9}");
out.println(".numi,.numi_attn {text-align:right}");
out.println(".total td {font-weight:bold}");
out.println(".passedodd td {background-color: #0A0}");
out.println(".passedeven td {background-color: #3F3}");
out.println(".skippedodd td {background-color: #CCC}");
out.println(".skippedodd td {background-color: #DDD}");
out.println(".failedodd td,.numi_attn {background-color: #F33}");
out.println(".failedeven td,.stripe .numi_attn {background-color: #D00}");
out.println(".stacktrace {white-space:pre;font-family:monospace}");
out.println(".totop {font-size:85%;text-align:center;border-bottom:2px solid #000}");
out.println("</style>");
out.println("</head>");
out.println("<body>");
}
/** Finishes HTML stream */
protected void endHtml(PrintWriter out) {
out.println("<center> Report customized by Hector Flores [hectorfb#gmail.com] </center>");
out.println("</body></html>");
}
// ~ Inner Classes --------------------------------------------------------
/** Arranges methods by classname and method name */
private class TestSorter implements Comparator<IInvokedMethod> {
// ~ Methods
// -------------------------------------------------------------
/** Arranges methods by classname and method name */
#Override
public int compare(IInvokedMethod o1, IInvokedMethod o2) {
// System.out.println("Comparing " + o1.getMethodName() + " " +
// o1.getDate()
// + " and " + o2.getMethodName() + " " + o2.getDate());
return (int) (o1.getDate() - o2.getDate());
// int r = ((T) o1).getTestClass().getName().compareTo(((T)
// o2).getTestClass().getName());
// if (r == 0) {
// r = ((T) o1).getMethodName().compareTo(((T) o2).getMethodName());
// }
// return r;
}
}
}
With those steps you already have your listener ready to listen.
How to call it?
If you use testng.xml add the following lines:
<listeners>
<listener class-name='[your_class_path].MyReporterListener'/>
</listeners>
If you run your tests from a java class, add the following lines:
private final static MyReporterListener frl = new MyReporterListener();
TestNG testng = new TestNG();
testng.addListener(frl);
With those steps, when you execute your tests you'll have two emailable reports, customized and original.
Now it's time to pimp your report.
In my case I had to add error messages, parameters and times (start and end), because it's very useful if you want to paste on an excel file.
My customized report:
You have to mainly modify generateSuiteSummaryReport(List suites) function.
Play with that and ask me if you have any problem.
Make a you CSS and embed it in EmailableReporter.class. This class file can be found in TestNG folder> org.testNg.reporters> EmailableReporter.class.
There you can edit the Style of the TestNG report HTML file which starts with
protected void startHtml(PrintWriter out)
Better you can use extent report html reporting library jar file. However i am using extentreport1.4.jar jar file. So you will get summary details at right corner of your report
Using the above code, I am facing the below null pointer issue, HTML report works fine when fewer tests are included in testng.xml, but when I include more tests, the HTML report does not get generated and the below exception is thrown
[TestNG] Reporter report.MyListener#60e07aed failed
java.lang.NullPointerException: Cannot invoke "String.length()" because "s" is null
at java.base/java.io.StringReader.<init>(StringReader.java:51)
at java.base/java.util.Scanner.<init>(Scanner.java:766)
at report.MyListener.getShortException(MyListener.java:294)
at report.MyListener.generateSuiteSummaryReport(MyListener.java:473)
at report.MyListener.generateReport(MyListener.java:72)
at org.testng.TestNG.generateReports(TestNG.java:1093)
at org.testng.TestNG.run(TestNG.java:1036)
at org.testng.remote.AbstractRemoteTestNG.run(AbstractRemoteTestNG.java:115)
at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:251)
at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:77)
To fix the above added a null check to avoid Null Pointer at line 295
if(str!=null) {
scanner = new Scanner(str);
String firstLine = scanner.nextLine()+"<br>";
m_out.println(firstLine);
}