[問題] Override new/delete 與 3rd party library
開發平台(Platform): (Ex: VC++, GCC, Linux, ...)
VC++ (C++11)
額外使用到的函數庫(Library Used): (Ex: OpenGL, ...)
Qt 5.6
問題(Question):
為了查一個記憶體問題, 嘗試 override new/delete 並記錄 new/delete 的配對
結果在使用 Qt 的某些 function 時會出現 delete 被傳入未經過我 new operator
配置的記憶體.
我的問題是, override new/delete 應該要直接 hide 原本的 new/delete 才對, 但
是為什麼會產生這種情況呢? 跟使用了 Qt library 這類 3rd party library 有關嗎?
Qt 內部理論上應該全部使用的是 placement new/delete, 實際上 trigger assert 的
也是其回傳 std::string 的 destructor
Override 的內容大略如下:
overridenew.cpp ==================================
static MyMap<void*, size_t> gMemMap;
void *operator new(size_t Size) {
void *p = malloc(Size);
if (p == 0) throw std::bad_alloc();
gMemMap[p] = Size;
return p;
}
void operator delete(void *p) {
if (p == 0) return;
assert(gMemMap.contains(p));
free(p);
}
main.cpp ========================================
int main() {
QString FileName("D:/testerfile_debug/test.log");
QByteArray Array = FileName.toUtf8();
// 下面這行可以正常執行
printf("%s", std::string(Array.constData(), Array.length()).c_str());
// 下面這行會在 std::string 解構時發生 assertion failed
printf("%s", Array.toStdString().c_str());
return 0;
}
我有想過幾個原因, 雖然 QByteArray::toStdString() 是一個 inline function
但是否有機會因為 build Qt 時使用的環境與實際使用的 VC++ 不同而產生呼叫到
不同 new/delete 的情況? (不過 Qt 是自己 build 的理論上應該是一樣的環境啦...)
或者有其它可能的原因, 不知道各位先進有沒有什麼想法呢?
P.S. 我後來使用 Windows 的 CRT Debug Heap 相關工具指出, 傳進來的 p 確定是
有 allocated 的, 只是它沒經過我的 new...
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 125.227.250.76
※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1464248924.A.5E9.html
推
05/26 16:58, , 1F
05/26 16:58, 1F
→
05/26 16:58, , 2F
05/26 16:58, 2F
我看 http://en.cppreference.com/w/cpp/memory/new/operator_new
寫道:
The single-object version (1) is directly called by the standard library
implementations of all other versions (2-4), so replacing that one function
is sufficient to handle all allocations. (since C++11)
所以理論上應該 override 一個就可以了?
剛測試了一下, new/delete array 的確會呼叫到我 override 的版本
→
05/26 18:33, , 3F
05/26 18:33, 3F
→
05/26 18:35, , 4F
05/26 18:35, 4F
我有追到這段 code XD
不過 assertion failed 的 call stack 是從 std::string::~string 來的
應該是 std::string inner buffer 解構時呼叫的
奇怪的是, 如果我把 toStdString() 移到我的程式裡面
將 const QByteArray& 當作參數
它就會正確的 call 到我的 new ...
→
05/26 23:29, , 5F
05/26 23:29, 5F
→
05/26 23:29, , 6F
05/26 23:29, 6F
→
05/26 23:29, , 7F
05/26 23:29, 7F
→
05/26 23:30, , 8F
05/26 23:30, 8F
不知道耶, 有沒有證明這個可能性的方法啊
直接看 assembly code 嗎 XD
我試試看, 沒想過這個方式
→
05/27 04:29, , 9F
05/27 04:29, 9F
喔喔~ 為何會想到這個方法呢? XD
曾經 static link 常連不起來, 晚點來試試 ""
→
05/27 11:35, , 10F
05/27 11:35, 10F
找到原因了, 貼在下面, 不確定是否跟您說的有關?
=======================================================
找到問題了, 先說結論:
我的 Qt lib 也有 override new operator 導致 undefined behavior
不確定怎麼會產生這樣的結果的, 不過直接從 assembly 追進去看,
QtCored 有一段這樣的東西:
Qt5Cored!ILT+93430(??0QVariantQAEMZ):
0x66a97cfb jmp Qt5Cored!QVariant::QVariant (66dc81a0)
Qt5Cored!ILT+93435(??2YAPAXIZ):
0x66a97d00 jmp Qt5Cored!operator new (66ee4890)
Qt5Cored!ILT+93440(??$copyVconst_iterator?$QListVQByte...
0x66a97d05 jmp Qt5Cored!std::copy<QList<QByteArray>::const... (66beb610)
Qt5Cored!ILT+93445(__alloca_probe):
x66a97d0a jmp Qt5Cored!_chkstk (66ee6540)
QByteArray::toStdString() 的 std::string 會 call 0x66a97d00 這個位址
然後 0x66ee4890 這個 operator new 不是我的:
Qt5Cored!operator new [f:\dd\vctools\crt\vcstartup\src\heap\new_scalar.cpp]:
0x66ee4890 push ebp
0x66ee4891 <+0x0001> mov ebp,esp
0x66ee4893 <+0x0003> push ecx
0x66ee4894 <+0x0004> mov eax,dword ptr [ebp+8]
0x66ee4897 <+0x0007> push eax
0x66ee4898 <+0x0008> call Qt5Cored!malloc (66ee7bc8)
......
裡面的 debug info 居然還有不知道哪來的 f:\dd\vctools 詭異路徑...
不知道這是 Qt 自己的還是因為 build 的過程中什麼參數沒設好造成的就是了
(畢竟在 Qt source code 中沒有這個檔案...)
囧
推
05/27 13:04, , 11F
05/27 13:04, 11F
→
05/27 13:06, , 12F
05/27 13:06, 12F
所以... 這東東是 VC default 的 new 囉!?
理論上應該要被 override 吧, 怎麼會被叫到呢...
→
05/27 14:20, , 13F
05/27 14:20, 13F
→
05/27 14:20, , 14F
05/27 14:20, 14F
我去查查相關的討論好了... 多謝提供關鍵字 XD
之前只遇過 mix debug/release library 直接炸掉的情況
現在這個問題很隱晦啊
※ 編輯: Ebergies (125.227.250.76), 05/27/2016 14:56:41
C_and_CPP 近期熱門文章
PTT數位生活區 即時熱門文章