文件copy异常dump分析

背景

钉钉突然收到一个crash的告警,然后下载了dump分析。

过程

使用

1
.ecxr 查看异常上下文

upload successful

很明显是由于 调用了 std::copy_file 函数抛出了异常。

首先查看指定路径下 filesystem 的源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[[noreturn]] inline void _Throw_fs_error(
const char* _Op, __std_win_error _Error, const path& _Path1, const path& _Path2) {
_THROW(filesystem_error(_Op, _Path1, _Path2, _Make_ec(_Error)));
}

inline bool copy_file(const path& _From, const path& _To, const copy_options _Options) {
// copy a file _From -> _To according to _Options
const auto _Result =
__std_fs_copy_file(_From.c_str(), _To.c_str(), static_cast<__std_fs_copy_options>(_Options));
if (_Result._Error != __std_win_error::_Success) {
_Throw_fs_error("copy_file", _Result._Error, _From, _To);
}

return _Result._Copied;
}

copy_file 函数调用了std_fs_copy_file,结果返回 != std_win_error::_Success 结果导致异常。

查看__std_win_error 错误类型有哪些

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
enum class __std_win_error : unsigned long { 
_Success = 0, // #define ERROR_SUCCESS 0L
_Invalid_function = 1,// #define ERROR_INVALID_FUNCTION 1L
_File_not_found = 2,// #define ERROR_FILE_NOT_FOUND 2L
_Path_not_found = 3, // #define ERROR_PATH_NOT_FOUND 3L
_Access_denied = 5, // #define ERROR_ACCESS_DENIED 5L
_Not_enough_memory = 8,// #define ERROR_NOT_ENOUGH_MEMORY 8L
_No_more_files = 18, // #define ERROR_NO_MORE_FILES 18L
_Sharing_violation = 32, // #define ERROR_SHARING_VIOLATION 32L
_Not_supported = 50, // #define ERROR_NOT_SUPPORTED 50L
_File_exists = 80, // #define ERROR_FILE_EXISTS 80L
_Invalid_parameter = 87, // #define ERROR_INVALID_PARAMETER 87L
_Insufficient_buffer = 122, // #define ERROR_INSUFFICIENT_BUFFER
122L _Invalid_name = 123, // #define ERROR_INVALID_NAME 123L
_Directory_not_empty = 145, // #define ERROR_DIR_NOT_EMPTY 145L
_Already_exists = 183, // #define ERROR_ALREADY_EXISTS 183L
_Filename_exceeds_range = 206, // #define ERROR_FILENAME_EXCED_RANGE 206L
_Directory_name_is_invalid = 267, // #define ERROR_DIRECTORY 267L
_Max = ~0UL // sentinel not used by Win32 };

  • 先查看 From “d://xxx.ini” To “d://xxx.ini.bak” 参数没有问题,可以排除以上大部分错误

  • 最后应该只剩下 _File_not_found 可能性最大了

历史记忆

说到这个问题,不得不说一直使用的windows函数 WritePrivateProfileString/GetPrivateProfileInt 函数。这个函数单独使用并不存在问题,但是如果和fopen 等函数配合使用时,就会出现问题。

比如A线程在WritePrivateProfileString/GetPrivateProfileInt,而B线程写模式fopen相同的文件,那么fopen成功之后,调用fwrite/fflush 会失败,并会把文件内容清空;错误原因非常相似。

修改方法

因为 copy_file 并不是必须的,所以

  • 1、增加try_catch 保护

  • 2、WritePrivateProfileString/GetPrivateProfileInt 这类函数替换成自己重载的 memini::GetPrivateProfileInt/WritePrivateProfileString [https://github.com/yingchengpa/memIni]