I have a non sistematic crash (R6016) when our application is about to terminate.
The application is composed of several DLL.
After a little investigation, we found the problem inside a function of mscvrt ( VS 2010) :
_ptiddata __cdecl _getptd_noexit (
void
)
{
_ptiddata ptd;
DWORD TL_LastError;
TL_LastError = GetLastError();
#ifdef _M_IX86
/*
* Initialize FlsGetValue function pointer in TLS by calling __set_flsgetvalue()
*/
if ( (ptd = (__set_flsgetvalue())(__flsindex)) == NULL ) {
#else /* _M_IX86 */
if ( (ptd = FLS_GETVALUE(__flsindex)) == NULL ) {
#endif /* _M_IX86 */
/*
* no per-thread data structure for this thread. try to create
* one.
*/
#ifdef _DEBUG
extern void * __cdecl _calloc_dbg_impl(size_t, size_t, int, const char *, int, int *);
if ((ptd = _calloc_dbg_impl(1, sizeof(struct _tiddata), _CRT_BLOCK, __FILE__, __LINE__, NULL)) != NULL) {
#else /* _DEBUG */
if ((ptd = _calloc_crt(1, sizeof(struct _tiddata))) != NULL) {
#endif /* _DEBUG */
if (FLS_SETVALUE(__flsindex, (LPVOID)ptd) ) {
/*
* Initialize of per-thread data
*/
_initptd(ptd,NULL);
ptd->_tid = GetCurrentThreadId();
ptd->_thandle = (uintptr_t)(-1);
}
else {
/*
* Return NULL to indicate failure
*/
_free_crt(ptd);
ptd = NULL;
}
}
}
SetLastError(TL_LastError);
return(ptd);
}
This function is called normally during process shutdown and normally it works.
When it doesn't work it returns NULL; analyzing the code the point of failure are:
calloc_crt returns NULL, ie it can't alloc memory
FLS_SETVALUE returns 0
FLS_SETVALUE is a macro that calls FlsSetValue. It receives a global var, __flsindex, that is the index for fiber
allocated with FlsAlloc.
__flsindex is resetted at termination of process in the ctr function of _mtterm
I think that crt is shutting down prematurely, before another DLL that use it but i don't know why.
Our program doesn't call CreateThread.
So my question is:
how dll are terminated and in which order? is it deterministic or changes from execution to execution?
how can I investigate further?
Related
I am trying to write a .NET Core host using coreclr.h. To do this I am trying to create function pointers to the c# code. I am able to call the static methods from my host, but calling the methods that depend on an object directly are not able to be called, ideally I would like to be able to call the constructor and all non-static methods from the C++ without modifying the C#. I can call Multiply5 and Main fine, but there is a segfault when the Program constructor or Add is called, is there any way to fix this? This is a Linux system so C++/CLI is not an option.
C++
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include "coreclrhost.h"
#include <iostream>
#define MANAGED_ASSEMBLY "TestConsole.dll"
#include <dirent.h>
#include <dlfcn.h>
#include <limits.h>
#define FS_SEPARATOR "/"
#define PATH_DELIMITER ":"
#define MAX_PATH PATH_MAX
#define CORECLR_FILE_NAME "libcoreclr.so"
// Function pointer types for the managed call and callback
typedef int (*report_callback_ptr)(int progress);
typedef char* (*doWork_ptr)(const char* jobName, int iterations, int dataSize, double* data, report_callback_ptr callbackFunction);
typedef int (*Multiply5_ptr)(const int i);
typedef (*Constructor_ptr)(int i1, int i2);
typedef int (*ReturnInt_ptr)();
void BuildTpaList(const char* directory, const char* extension, std::string& tpaList);
int main(int argc, char* argv[])
{
// Get the current executable's directory
// This sample assumes that both CoreCLR and the
// managed assembly to be loaded are next to this host
// so we need to get the current path in order to locate those.
char runtimePath[MAX_PATH];
#if WINDOWS
GetFullPathNameA(argv[0], MAX_PATH, runtimePath, NULL);
#elif LINUX
realpath(argv[0], runtimePath);
#endif
char *last_slash = strrchr(runtimePath, FS_SEPARATOR[0]);
if (last_slash != NULL)
*last_slash = 0;
// Construct the CoreCLR path
// For this sample, we know CoreCLR's path. For other hosts,
// it may be necessary to probe for coreclr.dll/libcoreclr.so
std::string coreClrPath(runtimePath);
coreClrPath.append(FS_SEPARATOR);
coreClrPath.append(CORECLR_FILE_NAME);
// Construct the managed library path
std::string managedLibraryPath(runtimePath);
managedLibraryPath.append(FS_SEPARATOR);
managedLibraryPath.append(MANAGED_ASSEMBLY);
//
// STEP 1: Load CoreCLR (coreclr.dll/libcoreclr.so)
//
#if WINDOWS
// <Snippet1>
HMODULE coreClr = LoadLibraryExA(coreClrPath.c_str(), NULL, 0);
// </Snippet1>
#elif LINUX
void *coreClr = dlopen(coreClrPath.c_str(), RTLD_NOW | RTLD_LOCAL);
#endif
if (coreClr == NULL)
{
printf("ERROR: Failed to load CoreCLR from %s\n", coreClrPath.c_str());
return -1;
}
else
{
printf("Loaded CoreCLR from %s\n", coreClrPath.c_str());
}
//
// STEP 2: Get CoreCLR hosting functions
//
#if WINDOWS
// <Snippet2>
coreclr_initialize_ptr initializeCoreClr = (coreclr_initialize_ptr)GetProcAddress(coreClr, "coreclr_initialize");
coreclr_create_delegate_ptr createManagedDelegate = (coreclr_create_delegate_ptr)GetProcAddress(coreClr, "coreclr_create_delegate");
coreclr_shutdown_ptr shutdownCoreClr = (coreclr_shutdown_ptr)GetProcAddress(coreClr, "coreclr_shutdown");
// </Snippet2>
#elif LINUX
coreclr_initialize_ptr initializeCoreClr = (coreclr_initialize_ptr)dlsym(coreClr, "coreclr_initialize");
coreclr_create_delegate_ptr createManagedDelegate = (coreclr_create_delegate_ptr)dlsym(coreClr, "coreclr_create_delegate");
coreclr_shutdown_ptr shutdownCoreClr = (coreclr_shutdown_ptr)dlsym(coreClr, "coreclr_shutdown");
#endif
if (initializeCoreClr == NULL)
{
printf("coreclr_initialize not found");
return -1;
}
if (createManagedDelegate == NULL)
{
printf("coreclr_create_delegate not found");
return -1;
}
if (shutdownCoreClr == NULL)
{
printf("coreclr_shutdown not found");
return -1;
}
//
// STEP 3: Construct properties used when starting the runtime
//
// Construct the trusted platform assemblies (TPA) list
// This is the list of assemblies that .NET Core can load as
// trusted system assemblies.
// For this host (as with most), assemblies next to CoreCLR will
// be included in the TPA list
std::string tpaList;
BuildTpaList(runtimePath, ".dll", tpaList);
tpaList.append(managedLibraryPath);
tpaList.append(":");
// <Snippet3>
// Define CoreCLR properties
// Other properties related to assembly loading are common here,
// but for this simple sample, TRUSTED_PLATFORM_ASSEMBLIES is all
// that is needed. Check hosting documentation for other common properties.
const char* propertyKeys[] = {
"TRUSTED_PLATFORM_ASSEMBLIES" // Trusted assemblies
};
const char* propertyValues[] = {
tpaList.c_str()
};
// </Snippet3>
//
// STEP 4: Start the CoreCLR runtime
//
// <Snippet4>
void* hostHandle;
unsigned int domainId;
// This function both starts the .NET Core runtime and creates
// the default (and only) AppDomain
int hr = initializeCoreClr(
runtimePath, // App base path
"SampleHost", // AppDomain friendly name
sizeof(propertyKeys) / sizeof(char*), // Property count
propertyKeys, // Property names
propertyValues, // Property values
&hostHandle, // Host handle
&domainId); // AppDomain ID
// </Snippet4>
if (hr >= 0)
{
printf("CoreCLR started\n");
}
else
{
printf("coreclr_initialize failed - status: 0x%08x\n", hr);
return -1;
}
//
// STEP 5: Create delegate to managed code and invoke it
//
// <Snippet5>
Multiply5_ptr managedDelegate;
// The assembly name passed in the third parameter is a managed assembly name
// as described at https://learn.microsoft.com/dotnet/framework/app-domains/assembly-names
hr = createManagedDelegate(
hostHandle,
domainId,
"TestConsole, Version=1.0.0.0",
"TestConsole.Program",
"Multiply5",
(void**)&managedDelegate);
// </Snippet5>
if (hr >= 0)
{
printf("Managed delegate created\n");
}
else
{
printf("coreclr_create_delegate failed - status: 0x%08x\n", hr);
return -1;
}
int i = 20;
// Invoke the managed delegate and write the returned intS to the console
//char* ret = managedDelegate("Test job", 1, sizeof(int), i, ReportProgressCallback);
int ret = managedDelegate(i);
printf("Managed code returned: %d\n", ret);
Constructor_ptr programDelegate;
hr = createManagedDelegate(hostHandle,
domainId,
"TestConsole, Version=1.0.0.0",
"TestConsole.Program",
"Program",
(void**)&programDelegate);
int i1 = i;
int i2 = ret;
programDelegate(i1,i2);//Will seg fault here
ReturnInt_ptr addDelegate;
hr = createManagedDelegate(hostHandle,
domainId,
"TestConsole, Version=1.0.0.0",
"TestConsole.Program",
"Add",
(void**)&addDelegate);
i = addDelegate(); //Also triggers a seg fault.
printf("Managed code returned: %d\n", i);
// Strings returned to native code must be freed by the native code
#if WINDOWS
CoTaskMemFree(ret);
#elif LINUX
// free(ret);
#endif
//
// STEP 6: Shutdown CoreCLR
//
// <Snippet6>
hr = shutdownCoreClr(hostHandle, domainId);
// </Snippet6>
if (hr >= 0)
{
printf("CoreCLR successfully shutdown\n");
}
else
{
printf("coreclr_shutdown failed - status: 0x%08x\n", hr);
}
return 0;
}
#if WINDOWS
// Win32 directory search for .dll files
// <Snippet7>
void BuildTpaList(const char* directory, const char* extension, std::string& tpaList)
{
// This will add all files with a .dll extension to the TPA list.
// This will include unmanaged assemblies (coreclr.dll, for example) that don't
// belong on the TPA list. In a real host, only managed assemblies that the host
// expects to load should be included. Having extra unmanaged assemblies doesn't
// cause anything to fail, though, so this function just enumerates all dll's in
// order to keep this sample concise.
std::string searchPath(directory);
searchPath.append(FS_SEPARATOR);
searchPath.append("*");
searchPath.append(extension);
WIN32_FIND_DATAA findData;
HANDLE fileHandle = FindFirstFileA(searchPath.c_str(), &findData);
if (fileHandle != INVALID_HANDLE_VALUE)
{
do
{
// Append the assembly to the list
tpaList.append(directory);
tpaList.append(FS_SEPARATOR);
tpaList.append(findData.cFileName);
tpaList.append(PATH_DELIMITER);
// Note that the CLR does not guarantee which assembly will be loaded if an assembly
// is in the TPA list multiple times (perhaps from different paths or perhaps with different NI/NI.dll
// extensions. Therefore, a real host should probably add items to the list in priority order and only
// add a file if it's not already present on the list.
//
// For this simple sample, though, and because we're only loading TPA assemblies from a single path,
// and have no native images, we can ignore that complication.
}
while (FindNextFileA(fileHandle, &findData));
FindClose(fileHandle);
}
}
// </Snippet7>
#elif LINUX
// POSIX directory search for .dll files
void BuildTpaList(const char* directory, const char* extension, std::string& tpaList)
{
DIR* dir = opendir(directory);
struct dirent* entry;
int extLength = strlen(extension);
while ((entry = readdir(dir)) != NULL)
{
// This simple sample doesn't check for symlinks
std::string filename(entry->d_name);
// Check if the file has the right extension
int extPos = filename.length() - extLength;
if (extPos <= 0 || filename.compare(extPos, extLength, extension) != 0)
{
continue;
}
// Append the assembly to the list
tpaList.append(directory);
tpaList.append(FS_SEPARATOR);
tpaList.append(filename);
tpaList.append(PATH_DELIMITER);
// Note that the CLR does not guarantee which assembly will be loaded if an assembly
// is in the TPA list multiple times (perhaps from different paths or perhaps with different NI/NI.dll
// extensions. Therefore, a real host should probably add items to the list in priority order and only
// add a file if it's not already present on the list.
//
// For this simple sample, though, and because we're only loading TPA assemblies from a single path,
// and have no native images, we can ignore that complication.
}
}
#endif
C#
namespace TestConsole
{
public class Program
{
IntTest i;
Program(int i1, int i2){
i = new IntTest(i1,i2);
}
public static void Main()
{
Program p = new Program(23,12);
Console.WriteLine(p.Add());
}
// This test method doesn't actually do anything, it just takes some input parameters,
// waits (in a loop) for a bit, invoking the callback function periodically, and
// then returns a string version of the double[] passed in.
//[return: MarshalAs(UnmanagedType.I4)]
public static int Return5(){
return 5;
}
public int Add(){
return i.Add();
}
private static int Multiply5(int i){
return 5*i;
}
}
}
IntTest is an external library.
So there is no way to do this all free c++, the other option is to wrap the C# method in a static one and create a function pointer that way.
Quick summary
In a nut shell i wish to access the debug information regarding the stack, preferably for passing information to Logger.
I wish for the information to tell me the Function Name, Line Number and File Name.
I've got the symbols and i'm attempting to access the junk values in them and turn them in to English. However nothing seems to work.
I have commented the code for people to read and see if they can help me effectively walk the stack to pull the information out i need.
So far i can point out SymGetModuleBase() does not return a positive number only 0, according to MSDN it fails if returns 0. Which is correct as it returns a memory address.
SymGetSymFromAddr() fails to return true, which i'm assuming gets the name of the stack frame/function
SymGetLineFromAddr() goes on to fail as well and doesn't return the line number location in the file and also doesn't gather the file path.
I believe this is due to the process parameter being invalid. I will elaborate below.
Attempts to locate and fix the problem
I have read the MSDN documentation repeatedly and feel like i'm banging my head off the wall, i've done pretty much what it said and i feel like it's just not working.
However i have noticed SymInitialize() should be called prior to attempting this, which i do call. This changed the GetLastError() value from 6 ERROR_INVALID_HANDLE to 0 ERROR_SUCCESS. Yet SymGetModuleBase() still returns 0 no matter if SymInitialize() although GetLastError() reports different error codes depending on SymInitialize() use. It should return a valid virtual memory address this is where i think the main problem lies in the code.
HANDLE process = ::GetCurrentProcess(); this line in the code below returns 0xffffffffffffffff very suspect if you ask me. This should return a pseudo virtual memory address but it to me anyway looks like a false result. This happens every time i run the program which leads me to think ::GetCurrentProcess() this is either got a bug, or doesn't work somehow. According to MSDN this is the correct a up to date way of getting the current process and i don't know how to get a valid HANDLE to a the process another way. So i can't pass the first parameter in SymGetModuleBase() the correct process, although i maybe wrong.
Full code for the function
void Logger::WriteStackFrames(log::TextColor tc)
{
// Initalize some memory
DWORD machine = IMAGE_FILE_MACHINE_AMD64;
HANDLE process = ::GetCurrentProcess();
HANDLE thread = GetCurrentThread();
// Initalize more memory
CONTEXT context;
STACKFRAME stack_frame;
// Set some memory
memset(&context, 0, sizeof(CONTEXT));
memset(&stack_frame, 0, sizeof(STACKFRAME));
// Capture the context
RtlCaptureContext(&context);
// Initalize a few things here and there
stack_frame.AddrPC.Offset = context.Rip;
stack_frame.AddrPC.Mode = AddrModeFlat;
stack_frame.AddrStack.Offset = context.Rsp;
stack_frame.AddrStack.Mode = AddrModeFlat;
stack_frame.AddrFrame.Offset = context.Rbp;
stack_frame.AddrFrame.Mode = AddrModeFlat;
// Randomly saw this was supposed to be called prior to StackWalk so tried it
if (!SymInitialize(process, 0, false))
{
wprintf(L"SymInitialize unable to find process!! Error: %d\r\n", GetLastError());
}
for (ULONG frame = 0; ; frame++)
{
// Set text color
SetTextColor(tc);
// Check for frames
BOOL result = StackWalk(machine, process, thread, &stack_frame, &context, 0,
SymFunctionTableAccess, SymGetModuleBase, 0);
// Get memory address of base module. Returns 0 although when SymInitialize is called before it the GetLastError returns 0 without return 6
DWORD64 module_base = SymGetModuleBase(process, stack_frame.AddrPC.Offset);
if (module_base == 0) {
wprintf(L"SymGetModuleBase is unable to get virutal address!! Error: %d\r\n", GetLastError());
}
// Initalize more memory
MODULEINFO module_info;
SecureZeroMemory(&module_info, sizeof(MODULEINFO));
// Get the file name of the file containing the function
TCHAR module_buffer[log::MaxPath];
DWORD mod_file = GetModuleFileName((HINSTANCE)module_base, module_buffer, log::MaxPath);
if ((module_base != 0) && (mod_file != 0))
{
module_info.module_name = module_buffer;
}
// Initalize more memory and clear it out
PIMAGEHLP_SYMBOL64 symbol;
IMAGEHLP_LINE64 line_num;
SecureZeroMemory(&symbol, sizeof(PIMAGEHLP_SYMBOL64));
SecureZeroMemory(&symbol, sizeof(IMAGEHLP_LINE64));
// Get the symbol
TCHAR symbol_buffer[log::MaxPath];
symbol = (PIMAGEHLP_SYMBOL)symbol_buffer;
symbol->SizeOfStruct = (sizeof(IMAGEHLP_SYMBOL) + log::MaxPath);
symbol->MaxNameLength = 254;
// Attempt to get name from symbol (fails)
LPSTR name_buffer = new CHAR[254];
if (SymGetSymFromAddr(process, stack_frame.AddrPC.Offset, 0, symbol))
{
name_buffer = symbol->Name;
}
// Set the size of something
DWORD offset = 0;
line_num.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
// Attempt to get the line and file name of where the symbol is
if (SymGetLineFromAddr(process, stack_frame.AddrPC.Offset, &offset, &line_num))
{
module_info.line = line_num.LineNumber;
module_info.file = line_num.FileName;
}
// Initalize memory
LPWSTR console_message = new TCHAR[log::MaxMsgLength];
LPWSTR file_message = new TCHAR[log::MaxMsgLength];
// Set some strings
swprintf(console_message, log::MaxMsgLength, L">> Frame %02lu: called from: %016X Stack: %016X Frame: %016X Address return: %016X\r\n",
frame, stack_frame.AddrPC.Offset, stack_frame.AddrStack.Offset, stack_frame.AddrFrame.Offset, stack_frame.AddrReturn.Offset);
swprintf(file_message, log::MaxMsgLength, L"Frame %02lu: called from: %016X Stack: %016X Frame: %016X Address return: %016X\r\n",
frame, stack_frame.AddrPC.Offset, stack_frame.AddrStack.Offset, stack_frame.AddrFrame.Offset, stack_frame.AddrReturn.Offset);
/* When the symbol can yield the name, line and file name the above strings
will also include that information */
// To go here . . .
// Write some strings
wprintf(console_message);
WriteAsync(file_message);
// Delete some memory
if (console_message) {
delete[] console_message; console_message = nullptr;
}
if (file_message) {
delete[] file_message; file_message = nullptr;
}
// If nothing else to do break loop
if (!result) {
break;
}
}
}
What i hope to achieve
Although i realize this will only work in debug mode that is fine, and i know i could write a macro using the __LINE__ __FUNCTION__ __FILE__ macros but that isn't what i'm looking for.
The results should be a wind up from the bottom stack showing the memory addresses of the calling PC, stack and frame. This works.
However it should also show me which the Name of the function, the Line number and the File path. This doesn't work.
FYI: I realize i need to add the code in to the generate the string and output it, but the code isn't capable of getting the information for the strings so that isn't coded in yet.
Please if anyone can help me, it would be fantastic all the code is focused around the "DbgHelp.h" windows file and most information is available on MSDN. So for the long question but i felt i should provide everything i know.
::GetCurrentProcess() = 0xffffffffffffffff
is not suspicious.
I tried a few variations on your code pulling bits from here and there - in the end I could not get it to work as I was using clang/mingw and it was not generating .pdb files. However, maybe the code will work for you as you are using MSVC. Anyway, here it is, in case it helps
I also noticed you hard coded machine type to AMD - I assume that is correct for you, but below I have an ifdef I found that sets it for other archs.
#include <windows.h>
#include <excpt.h>
#include <imagehlp.h>
#include <binutils/bfd.h>
#include <psapi.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdbool.h>
#include <psapi.h>
#include <dbghelp.h>
#define MAX_SYMBOL_LEN 1024
typedef struct CallstackEntry
{
DWORD64 offset; // if 0, we have no valid entry
CHAR name[MAX_SYMBOL_LEN];
CHAR undName[MAX_SYMBOL_LEN];
CHAR undFullName[MAX_SYMBOL_LEN];
DWORD64 offsetFromSmybol;
DWORD offsetFromLine;
DWORD lineNumber;
CHAR lineFileName[MAX_SYMBOL_LEN];
DWORD symType;
LPCSTR symTypeString;
CHAR moduleName[MAX_SYMBOL_LEN];
DWORD64 baseOfImage;
CHAR loadedImageName[MAX_SYMBOL_LEN];
} CallstackEntry;
typedef enum CallstackEntryType
{
firstEntry,
nextEntry,
lastEntry
} CallstackEntryType;
void _backtrace (void)
{
HANDLE process = ::GetCurrentProcess();
HANDLE thread = GetCurrentThread();
if (!SymInitialize(process, 0, true)) {
wprintf(L"SymInitialize unable to find process!! Error: %d\r\n",~
GetLastError());
}
DWORD symOptions = SymGetOptions();
symOptions |= SYMOPT_LOAD_LINES;
symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;
symOptions = SymSetOptions(symOptions);
char szSearchPath[MAX_SYMBOL_LEN] = {0};
SymGetSearchPath(process, szSearchPath, MAX_SYMBOL_LEN);
char szUserName[MAX_SYMBOL_LEN] = {0};
DWORD dwSize = MAX_SYMBOL_LEN;
GetUserNameA(szUserName, &dwSize);
CHAR search_path_debug[MAX_SYMBOL_LEN];
size_t maxLen = MAX_SYMBOL_LEN;
#if _MSC_VER >= 1400
maxLen = _TRUNCATE;
#endif
_snprintf_s(search_path_debug, maxLen,~
"SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n",
szSearchPath, symOptions, szUserName);
search_path_debug[MAX_SYMBOL_LEN - 1] = 0;
printf(search_path_debug);
// Initalize more memory
CONTEXT context;
memset(&context, 0, sizeof(CONTEXT));
context.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&context);
// Initalize a few things here and there
STACKFRAME stack;
memset(&stack, 0, sizeof(STACKFRAME));
stack.AddrPC.Offset = context.Rip;
stack.AddrPC.Mode = AddrModeFlat;
stack.AddrStack.Offset = context.Rsp;
stack.AddrStack.Mode = AddrModeFlat;
stack.AddrFrame.Offset = context.Rbp;
stack.AddrFrame.Mode = AddrModeFlat;
#ifdef _M_IX86
auto machine = IMAGE_FILE_MACHINE_I386;
#elif _M_X64
auto machine = IMAGE_FILE_MACHINE_AMD64;
#elif _M_IA64
auto machine = IMAGE_FILE_MACHINE_IA64;
#else
#error "platform not supported!"
#endif
for (ULONG frame = 0; ; frame++) {
BOOL result = StackWalk(machine,~
process,~
thread,~
&stack,
&context,
0,
SymFunctionTableAccess,~
SymGetModuleBase,~
0);
CallstackEntry csEntry;
csEntry.offset = stack.AddrPC.Offset;
csEntry.name[0] = 0;
csEntry.undName[0] = 0;
csEntry.undFullName[0] = 0;
csEntry.offsetFromSmybol = 0;
csEntry.offsetFromLine = 0;
csEntry.lineFileName[0] = 0;
csEntry.lineNumber = 0;
csEntry.loadedImageName[0] = 0;
csEntry.moduleName[0] = 0;
IMAGEHLP_SYMBOL64 symbol {};
symbol.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
symbol.MaxNameLength = MAX_SYMBOL_LEN;
// Initalize more memory and clear it out
if (SymGetSymFromAddr64(process,~
stack.AddrPC.Offset,
&csEntry.offsetFromSmybol,~
&symbol)) {
}
IMAGEHLP_LINE64 line {};
line.SizeOfStruct = sizeof(line);
if (SymGetLineFromAddr64(process,~
stack.AddrPC.Offset,
&csEntry.offsetFromLine,~
&line)) {
}
printf("Frame %lu:\n"
" Symbol name: %s\n"
" PC address: 0x%08LX\n"
" Stack address: 0x%08LX\n"
" Frame address: 0x%08LX\n"
"\n",
frame,
symbol.Name,
(ULONG64)stack.AddrPC.Offset,
(ULONG64)stack.AddrStack.Offset,
(ULONG64)stack.AddrFrame.Offset
);
// If nothing else to do break loop
if (!result) {
break;
}
}
}
I am working with YRDKRL78 G13 board. I am using IAR v.2.20 for compiler and ı have created a C++ project on it. And I am using one framework. Anyway I have implemented all my codes and which is working correctly but after 2 minutes IAR is giving information as "The Application is aborted" and "Program exit is reached." I am really confuse about it I used very large stack as 512 and near is 1024 far is the 4096.
Here is my main.cpp
#include "System.h"
extern "C"
{
#include "r_cg_macrodriver.h"
}
#pragma location = "OPTBYTE"
__root const uint8_t opbyte0 = 0x7EU;
#pragma location = "OPTBYTE"
__root const uint8_t opbyte1 = 0xFFU;
#pragma location = "OPTBYTE"
__root const uint8_t opbyte2 = 0xE8U;
#pragma location = "OPTBYTE"
__root const uint8_t opbyte3 = 0x85U;
/* Set security ID */
#pragma location = "SECUID"
__root const uint8_t secuid[10] =
{0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U};
void main(void)
{
System::SystemInstance().SYS_vInit();
System::SystemInstance().SYS_vStart();
System::SystemInstance().SYS_vRun();
while(1)
{
;
}
}
And here is the iar output. IAR Output Console
If somebody faced like this problem or if somebody has got any solution or idea could you please share with me here
Thanks for the help :)
EDIT:
#rjp Firstly thanks for response. I am using special framework on my board which Quantum leaps. these 3 functions are calling framwork functions and SYS_vRUN is calling endless loop
int_t QF::run(void) {
onStartup(); // startup callback
// the combined event-loop and background-loop of the QV kernel
for (;;) {
R_WDT_Restart();
RepaintLCD();
delay_ms(50); /* Leave some room for the system to respond */
QF_INT_DISABLE();
if (QV_readySet_.notEmpty()) {
uint_fast8_t p = QV_readySet_.findMax();
QMActive *a = active_[p];
QF_INT_ENABLE();
// perform the run-to-completion (RTS) step...
// 1. retrieve the event from the AO's event queue, which by this
// time must be non-empty and The "Vanialla" kernel asserts it.
// 2. dispatch the event to the AO's state machine.
// 3. determine if event is garbage and collect it if so
//
QEvt const *e = a->get_();
a->dispatch(e);
gc(e);
}
else {
// QV::onIdle() must be called with interrupts DISABLED because
// the determination of the idle condition (no events in the
// queues) can change at any time by an interrupt posting events
// to a queue. QV::onIdle() MUST enable interrupts internally,
// perhaps at the same time as putting the CPU into a power-saving
// mode.
QP::QV::onIdle();
}
}
}
End I searched all code for exit() function there is no any. But you mentioned another issue about assertion. here you can see assertion macro and custome implemented assertion function.
#define Q_ASSERT_ID(id_, test_) ((test_) \
? (void)0 : Q_onAssert(&Q_this_module_[0], (int_t)(id_)))
Function;
void Q_onAssert(char const Q_ROM * const file, int line) {
// implement the error-handling policy for your application!!!
QF_INT_DISABLE(); // disable all interrupts
// cause the reset of the CPU...
//WDTCTL = WDTPW | WDTHOLD;
//__asm(" push &0xFFFE");
// return from function does the reset
}
EDIT2:
Most dynamic memory processes are done in the LedFactory Class.
Header
/*
* LedFactory.h
*
* Created on: Aug 3, 2016
* Author: Dev
*/
#ifndef APPLICATION_LED_LEDFACTORY_H_
#define APPLICATION_LED_LEDFACTORY_H_
#include "LedController.h"
class LedFactory {
public:
typedef enum{
LED1,
LED2,
LED3,
LED4,
LED5,
LED6,
}LedTypes;
public:
LedFactory();
virtual ~LedFactory();
LedController * FirstLedFactory(LedTypes ledtype);
LedController * SecondLedFactory(LedTypes ledtype);
LedController * ThirdLedFactory(LedTypes ledtype);
LedController * FourthLedFactory(LedTypes ledtype);
LedController * FifthLedFactory(LedTypes ledtype);
LedController * SixthLedFactory(LedTypes ledtype);
public:
static LedFactory& instance();
};
#endif /* APPLICATION_LED_LEDFACTORY_H_ */
Source file.
/*
* LedFactory.cpp
*
* Created on: Aug 3, 2016
* Author: Dev
*/
#include <LedFactory.h>
#include "FirstLed.h"
#include "SecondLed.h"
#include "ThirdLed.h"
#include "FourthLed.h"
#include "FifthLed.h"
#include "SixthLed.h"
LedFactory::LedFactory() {
// TODO Auto-generated constructor stub
}
LedFactory::~LedFactory() {
// TODO Auto-generated destructor stub
}
LedFactory& LedFactory::instance()
{
static LedFactory instance;
return instance;
}
LedController * LedFactory::FirstLedFactory(LedTypes ledtype)
{
if(ledtype == (LedTypes)LED1)
{
return new FirstLed;
}
return NULL;
}
LedController * LedFactory::SecondLedFactory(LedTypes ledtype)
{
if(ledtype == (LedTypes)LED2)
return new SecondLed;
return NULL;
}
LedController * LedFactory::ThirdLedFactory(LedTypes ledtype)
{
if(ledtype == (LedTypes)LED3)
{
return new ThirdLed;
}
return NULL;
}
LedController * LedFactory::FourthLedFactory(LedTypes ledtype)
{
if(ledtype == (LedTypes)LED4)
{
return new FourthLed;
}
return NULL;
}
LedController * LedFactory::FifthLedFactory(LedTypes ledtype)
{
if(ledtype == (LedTypes)LED5)
{
return new FifthLed;
}
return NULL;
}
LedController * LedFactory::SixthLedFactory(LedTypes ledtype)
{
if(ledtype ==(LedTypes)LED6)
{
return new SixthLed;
}
return NULL;
}
Should I remove that class for increase the dynamic allocation memory issue ?
Or how can I fix this class ?
Problem has been solved. Main reason was the dynamic memory allocation processes which is used at the factory class.
I've created project using CMake in VS. Later I've add boost and gmock usage. GMock forced me to set /MT flag on all project in solution (main exe, plugins, dll, UT).
Now I'm facing strange problem. Log4Qt is my logging library. During startup when loggers are created deep inside this library
mpTextStream = new QTextStream(stdout);
where 'stdout' during runtime goes to
/* These functions are for enabling STATIC_CPPLIB functionality */
_CRTIMP FILE * __cdecl __iob_func(void)
{
return _iob;
}
which returns NULL (only first item in this _iob array isn't NULL). Ok, I see that those entries seems to be valid as initialization of this table is
FILE _iob[_IOB_ENTRIES] = {
/* _ptr, _cnt, _base, _flag, _file, _charbuf, _bufsiz */
/* stdin (_iob[0]) */
{ _bufin, 0, _bufin, _IOREAD | _IOYOURBUF, 0, 0, _INTERNAL_BUFSIZ },
/* stdout (_iob[1]) */
{ NULL, 0, NULL, _IOWRT, 1, 0, 0 },
...
So in the result when
void __cdecl _lock_file (
FILE *pf
)
{
/*
* The way the FILE (pointed to by pf) is locked depends on whether
* it is part of _iob[] or not
*/
if ( (pf >= _iob) && (pf <= (&_iob[_IOB_ENTRIES-1])) )
{
/*
* FILE lies in _iob[] so the lock lies in _locktable[].
*/
_lock( _STREAM_LOCKS + (int)(pf - _iob) );
/* We set _IOLOCKED to indicate we locked the stream */
pf->_flag |= _IOLOCKED;
}
else
/*
* Not part of _iob[]. Therefore, *pf is a _FILEX and the
* lock field of the struct is an initialized critical
* section.
*/
EnterCriticalSection( &(((_FILEX *)pf)->lock) );
}
stdout isn't recognized as part of _iob array and standard EnterCriticalSection is run which result in 'Unhandled exception at 0x77E58DC9 (ntdll.dll) in ComputerConfShop.exe: 0xC0000005: Access violation writing location 0x00000014.'
Should I add some specific building flag? Or gmock can be easily change from /MT to /MD library? Or something other?
I am using Spidermonkey 1.8.5 in my application.
My application crashes when I use the debug JS library. I am building the library with the following options:
--enable-debug --disable-optimize --enable-threadsafe
crash is pointing here:
Assertion failure: (cx)->thread->data.requestDepth || (cx)->thread == (cx)->runtime->gcThread, at ../../src/jsapi.cpp
Here is the sample program
/* Include the JSAPI header file to get access to SpiderMonkey. */
#include "jsapi.h"
/* The class of the global object. */
static JSClass global_class = {
"global", JSCLASS_GLOBAL_FLAGS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
/* The error reporter callback. */
void reportError(JSContext *cx, const char *message, JSErrorReport *report)
{
fprintf(stderr, "%s:%u:%s\n",
report->filename ? report->filename : "<no filename=\"filename\">",
(unsigned int) report->lineno,
message);
}
int main(int argc, const char *argv[])
{
/* JSAPI variables. */
JSRuntime *rt;
JSContext *cx;
JSObject *global;
printf("Started\n");
/* Create a JS runtime. You always need at least one runtime per process. */
rt = JS_NewRuntime(8 * 1024 * 1024);
if (rt == NULL)
return 1;
/*
* Create a context. You always need a context per thread.
* Note that this program is not multi-threaded.
*/
cx = JS_NewContext(rt, 8192);
if (cx == NULL)
return 1;
JS_SetErrorReporter(cx, reportError);
/*
* Create the global object in a new compartment.
* You always need a global object per context.
*/
global = JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL);
if (global == NULL)
return 1;
/*
* Populate the global object with the standard JavaScript
* function and object classes, such as Object, Array, Date.
*/
if (!JS_InitStandardClasses(cx, global))
return 1;
/* Your application code here. This may include JSAPI calls
* to create your own custom JavaScript objects and to run scripts.
*
* The following example code creates a literal JavaScript script,
* evaluates it, and prints the result to stdout.
*
* Errors are conventionally saved in a JSBool variable named ok.
*/
char *script = "'Hello ' + 'World!'";
jsval rval;
JSString *str;
JSBool ok;
const char *filename = "noname";
uintN lineno = 0;
ok = JS_EvaluateScript(cx, global, script, strlen(script),
filename, lineno, &rval);
if (rval == NULL | rval == JS_FALSE)
return 1;
str = JS_ValueToString(cx, rval);
printf("%s\n", JS_EncodeString(cx, str));
/* End of your application code */
/* Clean things up and shut down SpiderMonkey. */
JS_DestroyContext(cx);
JS_DestroyRuntime(rt);
JS_ShutDown();
return 0;
}
If I am building in release mode the program works fine. this problem is only with the debug mode. What am I missing here?
Insert JS_BeginRequest(cx); just before the JS_NewCompartmentAndGlobalObject call.
Insert JS_EndRequest(cx); just before the call to JS_DestroyContext.
Most JSAPI functions require a request. (I'm not sure why. The original reason for threads had to do with multithreading, but each JSRuntime is single-threaded now.)
The debug build of SpiderMonkey includes assertions that check API usage in ways that release mode can't. So you will likely see debug-only assertions again. I strongly recommend developing against a debug build, because those assertions almost always indicate real problems.
The next SpiderMonkey release is coming soon: https://bugzilla.mozilla.org/show_bug.cgi?id=735599#c54