From cd72720fd40ae306afe9292ed7329b075be9bc6a Mon Sep 17 00:00:00 2001 From: Konstantin Baladurin Date: Mon, 9 Oct 2017 20:21:22 +0300 Subject: [PATCH] [GDBJIT] Make gdbjit thread-safe & bug fix (dotnet/coreclr#14390) * [GDBJIT] Fix DW_AT_comp_dir setting We should use cuPath to set dirPath in NotifyGdb::EmitDebugInfo instead of DebugString[1]. * [GDBJIT] Make gdbjit thread-safe NotifyGdb::MethodPrepared method can be called from multiple threads simultaneously in this case gdbjit will work incorrectly as it uses global variable without synchronization. Commit migrated from https://github.com/dotnet/coreclr/commit/0ba964d19b84a690a451a61a38fa12c4ebf4f890 --- src/coreclr/src/inc/CrstTypes.def | 3 + src/coreclr/src/inc/crsttypes.h | 133 ++++++++++++------------ src/coreclr/src/vm/ceemain.cpp | 9 ++ src/coreclr/src/vm/gdbjit.cpp | 213 ++++++++++++++++++++++++-------------- src/coreclr/src/vm/gdbjit.h | 8 +- 5 files changed, 221 insertions(+), 145 deletions(-) diff --git a/src/coreclr/src/inc/CrstTypes.def b/src/coreclr/src/inc/CrstTypes.def index 5bf4ec6..8dc0d26 100644 --- a/src/coreclr/src/inc/CrstTypes.def +++ b/src/coreclr/src/inc/CrstTypes.def @@ -783,3 +783,6 @@ End Crst EventPipe AcquiredBefore ThreadIdDispenser ThreadStore DomainLocalBlock InstMethodHashTable End + +Crst NotifyGdb +End diff --git a/src/coreclr/src/inc/crsttypes.h b/src/coreclr/src/inc/crsttypes.h index 55dc5bd..1d6b349 100644 --- a/src/coreclr/src/inc/crsttypes.h +++ b/src/coreclr/src/inc/crsttypes.h @@ -1,6 +1,8 @@ +// // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +// #ifndef __CRST_TYPES_INCLUDED #define __CRST_TYPES_INCLUDED @@ -121,70 +123,71 @@ enum CrstType CrstNativeBinderInit = 104, CrstNativeImageCache = 105, CrstNls = 106, - CrstObjectList = 107, - CrstOnEventManager = 108, - CrstPatchEntryPoint = 109, - CrstPEFileSecurityManager = 110, - CrstPEImage = 111, - CrstPEImagePDBStream = 112, - CrstPendingTypeLoadEntry = 113, - CrstPinHandle = 114, - CrstPinnedByrefValidation = 115, - CrstProfilerGCRefDataFreeList = 116, - CrstProfilingAPIStatus = 117, - CrstPublisherCertificate = 118, - CrstRCWCache = 119, - CrstRCWCleanupList = 120, - CrstRCWRefCache = 121, - CrstReDacl = 122, - CrstReflection = 123, - CrstReJITDomainTable = 124, - CrstReJITGlobalRequest = 125, - CrstReJITSharedDomainTable = 126, - CrstRemoting = 127, - CrstRetThunkCache = 128, - CrstRWLock = 129, - CrstSavedExceptionInfo = 130, - CrstSaveModuleProfileData = 131, - CrstSecurityPolicyCache = 132, - CrstSecurityPolicyInit = 133, - CrstSecurityStackwalkCache = 134, - CrstSharedAssemblyCreate = 135, - CrstSharedBaseDomain = 136, - CrstSigConvert = 137, - CrstSingleUseLock = 138, - CrstSpecialStatics = 139, - CrstSqmManager = 140, - CrstStackSampler = 141, - CrstStressLog = 142, - CrstStrongName = 143, - CrstStubCache = 144, - CrstStubDispatchCache = 145, - CrstStubUnwindInfoHeapSegments = 146, - CrstSyncBlockCache = 147, - CrstSyncHashLock = 148, - CrstSystemBaseDomain = 149, - CrstSystemDomain = 150, - CrstSystemDomainDelayedUnloadList = 151, - CrstThreadIdDispenser = 152, - CrstThreadpoolEventCache = 153, - CrstThreadpoolTimerQueue = 154, - CrstThreadpoolWaitThreads = 155, - CrstThreadpoolWorker = 156, - CrstThreadStaticDataHashTable = 157, - CrstThreadStore = 158, - CrstTPMethodTable = 159, - CrstTypeEquivalenceMap = 160, - CrstTypeIDMap = 161, - CrstUMEntryThunkCache = 162, - CrstUMThunkHash = 163, - CrstUniqueStack = 164, - CrstUnresolvedClassLock = 165, - CrstUnwindInfoTableLock = 166, - CrstVSDIndirectionCellLock = 167, - CrstWinRTFactoryCache = 168, - CrstWrapperTemplate = 169, - kNumberOfCrstTypes = 170 + CrstNotifyGdb = 107, + CrstObjectList = 108, + CrstOnEventManager = 109, + CrstPatchEntryPoint = 110, + CrstPEFileSecurityManager = 111, + CrstPEImage = 112, + CrstPEImagePDBStream = 113, + CrstPendingTypeLoadEntry = 114, + CrstPinHandle = 115, + CrstPinnedByrefValidation = 116, + CrstProfilerGCRefDataFreeList = 117, + CrstProfilingAPIStatus = 118, + CrstPublisherCertificate = 119, + CrstRCWCache = 120, + CrstRCWCleanupList = 121, + CrstRCWRefCache = 122, + CrstReDacl = 123, + CrstReflection = 124, + CrstReJITDomainTable = 125, + CrstReJITGlobalRequest = 126, + CrstReJITSharedDomainTable = 127, + CrstRemoting = 128, + CrstRetThunkCache = 129, + CrstRWLock = 130, + CrstSavedExceptionInfo = 131, + CrstSaveModuleProfileData = 132, + CrstSecurityPolicyCache = 133, + CrstSecurityPolicyInit = 134, + CrstSecurityStackwalkCache = 135, + CrstSharedAssemblyCreate = 136, + CrstSharedBaseDomain = 137, + CrstSigConvert = 138, + CrstSingleUseLock = 139, + CrstSpecialStatics = 140, + CrstSqmManager = 141, + CrstStackSampler = 142, + CrstStressLog = 143, + CrstStrongName = 144, + CrstStubCache = 145, + CrstStubDispatchCache = 146, + CrstStubUnwindInfoHeapSegments = 147, + CrstSyncBlockCache = 148, + CrstSyncHashLock = 149, + CrstSystemBaseDomain = 150, + CrstSystemDomain = 151, + CrstSystemDomainDelayedUnloadList = 152, + CrstThreadIdDispenser = 153, + CrstThreadpoolEventCache = 154, + CrstThreadpoolTimerQueue = 155, + CrstThreadpoolWaitThreads = 156, + CrstThreadpoolWorker = 157, + CrstThreadStaticDataHashTable = 158, + CrstThreadStore = 159, + CrstTPMethodTable = 160, + CrstTypeEquivalenceMap = 161, + CrstTypeIDMap = 162, + CrstUMEntryThunkCache = 163, + CrstUMThunkHash = 164, + CrstUniqueStack = 165, + CrstUnresolvedClassLock = 166, + CrstUnwindInfoTableLock = 167, + CrstVSDIndirectionCellLock = 168, + CrstWinRTFactoryCache = 169, + CrstWrapperTemplate = 170, + kNumberOfCrstTypes = 171 }; #endif // __CRST_TYPES_INCLUDED @@ -302,6 +305,7 @@ int g_rgCrstLevelMap[] = -1, // CrstNativeBinderInit -1, // CrstNativeImageCache 0, // CrstNls + 0, // CrstNotifyGdb 2, // CrstObjectList 0, // CrstOnEventManager 0, // CrstPatchEntryPoint @@ -477,6 +481,7 @@ LPCSTR g_rgCrstNameMap[] = "CrstNativeBinderInit", "CrstNativeImageCache", "CrstNls", + "CrstNotifyGdb", "CrstObjectList", "CrstOnEventManager", "CrstPatchEntryPoint", diff --git a/src/coreclr/src/vm/ceemain.cpp b/src/coreclr/src/vm/ceemain.cpp index 011caf0..44f5d8e 100644 --- a/src/coreclr/src/vm/ceemain.cpp +++ b/src/coreclr/src/vm/ceemain.cpp @@ -234,6 +234,10 @@ #include "process.h" #endif // !FEATURE_PAL +#ifdef FEATURE_GDBJIT +#include "gdbjit.h" +#endif // FEATURE_GDBJIT + #ifdef FEATURE_IPCMAN static HRESULT InitializeIPCManager(void); static void PublishIPCManager(void); @@ -685,6 +689,11 @@ void EEStartupHelper(COINITIEE fFlags) EventPipe::Initialize(); #endif // FEATURE_PERFTRACING +#ifdef FEATURE_GDBJIT + // Initialize gdbjit + NotifyGdb::Initialize(); +#endif // FEATURE_GDBJIT + #ifdef FEATURE_EVENT_TRACE // Initialize event tracing early so we can trace CLR startup time events. InitializeEventTracing(); diff --git a/src/coreclr/src/vm/gdbjit.cpp b/src/coreclr/src/vm/gdbjit.cpp index 9dbf9df..22bf30a 100644 --- a/src/coreclr/src/vm/gdbjit.cpp +++ b/src/coreclr/src/vm/gdbjit.cpp @@ -661,15 +661,57 @@ void __attribute__((noinline)) __jit_debug_register_code() { __asm__(""); }; /* Make sure to specify the version statically, because the debugger may check the version before we can set it. */ struct jit_descriptor __jit_debug_descriptor = { 1, 0, 0, 0 }; +static CrstStatic g_jitDescriptorCrst; // END of GDB JIT interface -/* Static data for .debug_str section */ -const char* DebugStrings[] = { - "CoreCLR", "" /* module name */, "" /* module path */ -}; +class DebugStringsCU +{ +public: + DebugStringsCU(const char *module, const char *path) + : m_producerName("CoreCLR"), + m_moduleName(module), + m_moduleDir(path), + m_producerOffset(0), + m_moduleNameOffset(0), + m_moduleDirOffset(0) + { + } -const int DebugStringCount = sizeof(DebugStrings) / sizeof(DebugStrings[0]); + int GetProducerOffset() const { return m_producerOffset; } + int GetModuleNameOffset() const { return m_moduleNameOffset; } + int GetModuleDirOffset() const { return m_moduleDirOffset; } + + void DumpStrings(char *ptr, int &offset) + { + m_producerOffset = offset; + DumpString(m_producerName, ptr, offset); + + m_moduleNameOffset = offset; + DumpString(m_moduleName, ptr, offset); + + m_moduleDirOffset = offset; + DumpString(m_moduleDir, ptr, offset); + } + +private: + const char* m_producerName; + const char* m_moduleName; + const char* m_moduleDir; + + int m_producerOffset; + int m_moduleNameOffset; + int m_moduleDirOffset; + + static void DumpString(const char *str, char *ptr, int &offset) + { + if (ptr != nullptr) + { + strcpy(ptr + offset, str); + } + offset += strlen(str) + 1; + } +}; /* Static data for .debug_abbrev */ const unsigned char AbbrevTable[] = { @@ -1785,48 +1827,23 @@ static int getNextPrologueIndex(int from, T &arr, int n) return -1; } +static NewArrayHolder g_wszModuleNames; +static DWORD g_cBytesNeeded; + static inline bool isListedModule(const WCHAR *wszModuleFile) { - static NewArrayHolder wszModuleNames = nullptr; - static DWORD cBytesNeeded = 0; - - // Get names of interesting modules from environment - if (wszModuleNames == nullptr && cBytesNeeded == 0) - { - DWORD cCharsNeeded = GetEnvironmentVariableW(W("CORECLR_GDBJIT"), NULL, 0); - - if (cCharsNeeded == 0) - { - cBytesNeeded = 0xffffffff; - return false; - } - - WCHAR *wszModuleNamesBuf = new WCHAR[cCharsNeeded+1]; - - cCharsNeeded = GetEnvironmentVariableW(W("CORECLR_GDBJIT"), wszModuleNamesBuf, cCharsNeeded); - - if (cCharsNeeded == 0) - { - delete[] wszModuleNamesBuf; - cBytesNeeded = 0xffffffff; - return false; - } - - wszModuleNames = wszModuleNamesBuf; - cBytesNeeded = cCharsNeeded + 1; - } - else if (wszModuleNames == nullptr) + if (g_wszModuleNames == nullptr) { return false; } - _ASSERTE(wszModuleNames != nullptr && cBytesNeeded > 0); + _ASSERTE(g_cBytesNeeded > 0); BOOL isUserDebug = FALSE; - NewArrayHolder wszModuleName = new WCHAR[cBytesNeeded]; - LPWSTR pComma = wcsstr(wszModuleNames, W(",")); - LPWSTR tmp = wszModuleNames; + NewArrayHolder wszModuleName = new WCHAR[g_cBytesNeeded]; + LPWSTR pComma = wcsstr(g_wszModuleNames, W(",")); + LPWSTR tmp = g_wszModuleNames; while (pComma != NULL) { @@ -1854,7 +1871,8 @@ static inline bool isListedModule(const WCHAR *wszModuleFile) return isUserDebug; } -static NotifyGdb::AddrSet codeAddrs; +static NotifyGdb::AddrSet g_codeAddrs; +static CrstStatic g_codeAddrsCrst; class Elf_SectionTracker { @@ -2440,6 +2458,38 @@ static void BuildDebugFrame(Elf_Builder &elfBuilder, PCODE pCode, TADDR codeSize } #endif // FEATURE_GDBJIT_FRAME +void NotifyGdb::Initialize() +{ + g_jitDescriptorCrst.Init(CrstNotifyGdb); + g_codeAddrsCrst.Init(CrstNotifyGdb); + + // Get names of interesting modules from environment + if (g_wszModuleNames == nullptr && g_cBytesNeeded == 0) + { + DWORD cCharsNeeded = GetEnvironmentVariableW(W("CORECLR_GDBJIT"), NULL, 0); + + if (cCharsNeeded == 0) + { + g_cBytesNeeded = 0xffffffff; + return; + } + + WCHAR *wszModuleNamesBuf = new WCHAR[cCharsNeeded+1]; + + cCharsNeeded = GetEnvironmentVariableW(W("CORECLR_GDBJIT"), wszModuleNamesBuf, cCharsNeeded); + + if (cCharsNeeded == 0) + { + delete[] wszModuleNamesBuf; + g_cBytesNeeded = 0xffffffff; + return; + } + + g_wszModuleNames = wszModuleNamesBuf; + g_cBytesNeeded = cCharsNeeded + 1; + } +} + /* Create ELF/DWARF debug info for jitted method */ void NotifyGdb::MethodPrepared(MethodDesc* methodDescPtr) { @@ -2547,21 +2597,25 @@ void NotifyGdb::OnMethodPrepared(MethodDesc* methodDescPtr) jit_symbols->symfile_addr = symfile_addr; jit_symbols->symfile_size = symfile_size; - /* Link into list */ - jit_code_entry *head = __jit_debug_descriptor.first_entry; - __jit_debug_descriptor.first_entry = jit_symbols; - if (head != 0) { - jit_symbols->next_entry = head; - head->prev_entry = jit_symbols; - } + CrstHolder crst(&g_jitDescriptorCrst); - jit_symbols.SuppressRelease(); + /* Link into list */ + jit_code_entry *head = __jit_debug_descriptor.first_entry; + __jit_debug_descriptor.first_entry = jit_symbols; + if (head != 0) + { + jit_symbols->next_entry = head; + head->prev_entry = jit_symbols; + } - /* Notify the debugger */ - __jit_debug_descriptor.relevant_entry = jit_symbols; - __jit_debug_descriptor.action_flag = JIT_REGISTER_FN; - __jit_debug_register_code(); + jit_symbols.SuppressRelease(); + + /* Notify the debugger */ + __jit_debug_descriptor.relevant_entry = jit_symbols; + __jit_debug_descriptor.action_flag = JIT_REGISTER_FN; + __jit_debug_register_code(); + } } #ifdef FEATURE_GDBJIT_FRAME @@ -2764,27 +2818,24 @@ bool NotifyGdb::EmitDebugInfo(Elf_Builder &elfBuilder, MethodDesc* methodDescPtr if (dirLen != 0) { dirPath = new char[dirLen]; - memcpy(dirPath, DebugStrings[1], dirLen - 1); + memcpy(dirPath, cuPath, dirLen - 1); dirPath[dirLen - 1] = '\0'; } - DebugStrings[1] = fileName; - DebugStrings[2] = dirPath ? (const char *)dirPath : ""; + + DebugStringsCU debugStringsCU(fileName, dirPath ? (const char *)dirPath : ""); /* Build .debug_str section */ - if (!BuildDebugStrings(dbgStr, pTypeMap, method)) + if (!BuildDebugStrings(dbgStr, pTypeMap, method, debugStringsCU)) { return false; } /* Build .debug_info section */ - if (!BuildDebugInfo(dbgInfo, pTypeMap, method)) + if (!BuildDebugInfo(dbgInfo, pTypeMap, method, debugStringsCU)) { return false; } - DebugStrings[1] = ""; - DebugStrings[2] = ""; - for (int i = 0; i < method.GetCount(); ++i) { method[i]->lines = nullptr; @@ -2877,6 +2928,8 @@ void NotifyGdb::MethodPitched(MethodDesc* methodDescPtr) if (pCode == NULL) return; + CrstHolder crst(&g_jitDescriptorCrst); + /* Find relevant entry */ for (jit_code_entry* jit_symbols = __jit_debug_descriptor.first_entry; jit_symbols != 0; jit_symbols = jit_symbols->next_entry) { @@ -3197,7 +3250,7 @@ static void fixLineMapping(SymbolsInfo* lines, unsigned nlines) /* Build program for DWARF source line section */ bool NotifyGdb::BuildLineProg(MemBuf& buf, PCODE startAddr, TADDR codeSize, SymbolsInfo* lines, unsigned nlines) { - static char cnv_buf[16]; + char cnv_buf[16]; /* reserve memory assuming worst case: set address, advance line command, set proglogue/epilogue and copy for each line */ buf.MemSize = @@ -3266,15 +3319,15 @@ bool NotifyGdb::BuildLineProg(MemBuf& buf, PCODE startAddr, TADDR codeSize, Symb } /* Build the DWARF .debug_str section */ -bool NotifyGdb::BuildDebugStrings(MemBuf& buf, PTK_TypeInfoMap pTypeMap, FunctionMemberPtrArrayHolder &method) +bool NotifyGdb::BuildDebugStrings(MemBuf& buf, + PTK_TypeInfoMap pTypeMap, + FunctionMemberPtrArrayHolder &method, + DebugStringsCU &debugStringsCU) { int totalLength = 0; /* calculate total section size */ - for (int i = 0; i < DebugStringCount; ++i) - { - totalLength += strlen(DebugStrings[i]) + 1; - } + debugStringsCU.DumpStrings(nullptr, totalLength); for (int i = 0; i < method.GetCount(); ++i) { @@ -3297,11 +3350,8 @@ bool NotifyGdb::BuildDebugStrings(MemBuf& buf, PTK_TypeInfoMap pTypeMap, Functio /* copy strings */ char* bufPtr = buf.MemPtr; int offset = 0; - for (int i = 0; i < DebugStringCount; ++i) - { - strcpy(bufPtr + offset, DebugStrings[i]); - offset += strlen(DebugStrings[i]) + 1; - } + + debugStringsCU.DumpStrings(bufPtr, offset); for (int i = 0; i < method.GetCount(); ++i) { @@ -3332,7 +3382,10 @@ bool NotifyGdb::BuildDebugAbbrev(MemBuf& buf) } /* Build tge DWARF .debug_info section */ -bool NotifyGdb::BuildDebugInfo(MemBuf& buf, PTK_TypeInfoMap pTypeMap, FunctionMemberPtrArrayHolder &method) +bool NotifyGdb::BuildDebugInfo(MemBuf& buf, + PTK_TypeInfoMap pTypeMap, + FunctionMemberPtrArrayHolder &method, + DebugStringsCU &debugStringsCU) { int totalTypeVarSubSize = 0; { @@ -3366,9 +3419,9 @@ bool NotifyGdb::BuildDebugInfo(MemBuf& buf, PTK_TypeInfoMap pTypeMap, FunctionMe reinterpret_cast(buf.MemPtr + offset); memcpy(buf.MemPtr + offset, &debugInfoCU, sizeof(DebugInfoCU)); offset += sizeof(DebugInfoCU); - diCU->m_prod_off = 0; - diCU->m_cu_name = strlen(DebugStrings[0]) + 1; - diCU->m_cu_dir = diCU->m_cu_name + strlen(DebugStrings[1]) + 1; + diCU->m_prod_off = debugStringsCU.GetProducerOffset(); + diCU->m_cu_name = debugStringsCU.GetModuleNameOffset(); + diCU->m_cu_dir = debugStringsCU.GetModuleDirOffset(); { auto iter = pTypeMap->Begin(); while (iter != pTypeMap->End()) @@ -3422,8 +3475,10 @@ bool NotifyGdb::CollectCalledMethods(CalledMethod* pCalledMethods, { AddrSet tmpCodeAddrs; - if (!codeAddrs.Contains(nativeCode)) - codeAddrs.Add(nativeCode); + CrstHolder crst(&g_codeAddrsCrst); + + if (!g_codeAddrs.Contains(nativeCode)) + g_codeAddrs.Add(nativeCode); CalledMethod* pList = pCalledMethods; @@ -3431,7 +3486,7 @@ bool NotifyGdb::CollectCalledMethods(CalledMethod* pCalledMethods, while (pList != NULL) { TADDR callAddr = (TADDR)pList->GetCallAddr(); - if (!tmpCodeAddrs.Contains(callAddr) && !codeAddrs.Contains(callAddr)) { + if (!tmpCodeAddrs.Contains(callAddr) && !g_codeAddrs.Contains(callAddr)) { tmpCodeAddrs.Add(callAddr); } pList = pList->GetNext(); @@ -3445,7 +3500,7 @@ bool NotifyGdb::CollectCalledMethods(CalledMethod* pCalledMethods, while (i < symbolCount && pList != NULL) { TADDR callAddr = (TADDR)pList->GetCallAddr(); - if (!codeAddrs.Contains(callAddr)) + if (!g_codeAddrs.Contains(callAddr)) { MethodDesc* pMD = pList->GetMethodDesc(); LPCUTF8 methodName = pMD->GetName(); @@ -3455,7 +3510,7 @@ bool NotifyGdb::CollectCalledMethods(CalledMethod* pCalledMethods, sprintf_s((char*)symbolNames[i].m_name, symbolNameLength, "__thunk_%s", methodName); symbolNames[i].m_value = callAddr; ++i; - codeAddrs.Add(callAddr); + g_codeAddrs.Add(callAddr); } pList = pList->GetNext(); } diff --git a/src/coreclr/src/vm/gdbjit.h b/src/coreclr/src/vm/gdbjit.h index 6cfe52c..abb8480 100644 --- a/src/coreclr/src/vm/gdbjit.h +++ b/src/coreclr/src/vm/gdbjit.h @@ -336,11 +336,13 @@ public: struct Elf_Symbol; class Elf_Builder; +class DebugStringsCU; class NotifyGdb { public: class FileTableBuilder; + static void Initialize(); static void MethodPrepared(MethodDesc* methodDescPtr); static void MethodPitched(MethodDesc* methodDescPtr); template @@ -429,9 +431,11 @@ private: NewArrayHolder &symbolNames, int symbolCount, unsigned int thunkIndexBase); static bool BuildStringTableSection(MemBuf& strTab, NewArrayHolder &symbolNames, int symbolCount); - static bool BuildDebugStrings(MemBuf& buf, PTK_TypeInfoMap pTypeMap, FunctionMemberPtrArrayHolder &method); + static bool BuildDebugStrings(MemBuf& buf, PTK_TypeInfoMap pTypeMap, FunctionMemberPtrArrayHolder &method, + DebugStringsCU &debugStringsCU); static bool BuildDebugAbbrev(MemBuf& buf); - static bool BuildDebugInfo(MemBuf& buf, PTK_TypeInfoMap pTypeMap, FunctionMemberPtrArrayHolder &method); + static bool BuildDebugInfo(MemBuf& buf, PTK_TypeInfoMap pTypeMap, FunctionMemberPtrArrayHolder &method, + DebugStringsCU &debugStringsCU); static bool BuildDebugPub(MemBuf& buf, const char* name, uint32_t size, uint32_t dieOffset); static bool BuildLineTable(MemBuf& buf, PCODE startAddr, TADDR codeSize, SymbolsInfo* lines, unsigned nlines, const char * &cuPath); -- 2.7.4