/* * TraceFileIO.cpp * * This source file is part of the FoundationDB open source project * * Copyright 2013-2022 Apple Inc. and the FoundationDB project authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fdbrpc/TraceFileIO.h" #if CENABLED(0, NOT_IN_CLEAN) // TO RUN: Set the name of file you wish to track std::string debugFileName = "storage-3807bf710afaa6d39d23db60d6e687c8.fdb-wal"; std::map debugFileData; std::map debugFileMask; std::map debugFileRegions; uint8_t* debugFileSetPage; // Setup memory data structures for file debugging. // TO RUN: setup debugFileRegions map with the regions of the file you wish to track. Keys of the map are offsets in // the file, and values are the lengths of the region void debugFileSetup() { debugFileRegions[0] = 1e8; for (auto itr = debugFileRegions.begin(); itr != debugFileRegions.end(); ++itr) { debugFileData[itr->first] = (uint8_t*)malloc(itr->second); memset(debugFileData[itr->first], 0, itr->second); debugFileMask[itr->first] = (uint8_t*)malloc(itr->second); memset(debugFileMask[itr->first], 0, itr->second); } debugFileSetPage = (uint8_t*)malloc(4096); memset(debugFileSetPage, 1, 4096); } // Trim a file path to the file name std::string debugFileTrim(std::string filename) { int index = filename.find_last_of("/"); if (index == filename.npos) return filename; else return filename.substr(index + 1); } // Checks if a block of memory has been written bool debugFileIsSet(uint8_t* storeMask, int64_t offset, int64_t length) { for (int64_t i = 0; i < length; i += 4096) if (memcmp(&storeMask[offset + i], debugFileSetPage, std::min((int64_t)4096, length - i))) return false; return true; } // Checks that a given block of data is the same as what has been written by a call to debugFileSet void debugFileCheck(std::string context, std::string file, const void* data, int64_t offset, int length) { if (debugFileRegions.empty()) debugFileSetup(); if (debugFileTrim(file) == debugFileName) { bool found = false; for (auto itr = debugFileRegions.begin(); itr != debugFileRegions.end(); ++itr) { if (offset + length > itr->first && offset < itr->first + itr->second) { found = true; uint8_t* storeData = debugFileData[itr->first]; uint8_t* storeMask = debugFileMask[itr->first]; ASSERT(storeData && storeMask); int64_t dataOffset = std::max((int64_t)0, itr->first - offset); int64_t dbgOffset = std::max((int64_t)0, offset - itr->first); int64_t cmpLength = std::min(length - dataOffset, itr->second - dbgOffset); ASSERT(cmpLength > 0); TraceEvent("DebugFileCheck") .detail("Context", context) .detail("Filename", file) .detail("Offset", offset) .detail("Length", length); bool success = true; if (debugFileIsSet(storeMask, dbgOffset, cmpLength)) { //TraceEvent("DebugFileCheckMemCmp").detail("Context", context).detail("Filename", file).detail("Offset", offset).detail("Length", length).detail("DataOffset", dataOffset).detail("DbgOffset", dbgOffset).detail("OverlapLength", cmpLength); if (memcmp(&((uint8_t*)data)[dataOffset], &storeData[dbgOffset], cmpLength)) success = false; } else { TraceEvent("DebugFileUnsetCheck") .detail("Context", context) .detail("Filename", file) .detail("Offset", offset) .detail("Length", length); for (int64_t i = 0; i < cmpLength; i++) { if (storeMask[dbgOffset + i] && storeData[dbgOffset + i] != ((uint8_t*)data)[dataOffset + i]) { success = false; break; } } } if (!success) TraceEvent(SevWarnAlways, "DebugFileFail") .detail("Context", context) .detail("Filename", file) .detail("Offset", offset) .detail("Length", length); } } if (!found) TraceEvent("DebugFileSkippingCheck") .detail("Context", context) .detail("Filename", file) .detail("Offset", offset) .detail("Length", length); } } // Updates the in-memory copy of tracked data at a given offset void debugFileSet(std::string context, std::string file, const void* data, int64_t offset, int length) { if (debugFileRegions.empty()) debugFileSetup(); if (debugFileTrim(file) == debugFileName) { bool found = false; for (auto itr = debugFileRegions.begin(); itr != debugFileRegions.end(); ++itr) { if (offset + length > itr->first && offset < itr->first + itr->second) { found = true; uint8_t* storeData = debugFileData[itr->first]; uint8_t* storeMask = debugFileMask[itr->first]; ASSERT(storeData && storeMask); int64_t dataOffset = std::max((int64_t)0, itr->first - offset); int64_t dbgOffset = std::max((int64_t)0, offset - itr->first); int64_t cmpLength = std::min(length - dataOffset, itr->second - dbgOffset); ASSERT(cmpLength > 0); TraceEvent("DebugFileSet") .detail("Context", context) .detail("Filename", file) .detail("Offset", offset) .detail("Length", length); //TraceEvent("DebugFileSetMemCpy").detail("Context", context).detail("Filename", file).detail("Offset", offset).detail("Length", length).detail("DataOffset", dataOffset).detail("DbgOffset", dbgOffset).detail("OverlapLength", std::min(length - dataOffset, itr->second - dbgOffset)); memcpy(&storeData[dbgOffset], &((uint8_t*)data)[dataOffset], cmpLength); memset(&storeMask[dbgOffset], 1, cmpLength); } } if (!found) TraceEvent("DebugFileSkippingSet") .detail("Context", context) .detail("Filename", file) .detail("Offset", offset) .detail("Length", length); } } // Updates the in-memory copy of tracked data to account for truncates (this simply invalidates any data after truncate // point) void debugFileTruncate(std::string context, std::string file, int64_t offset) { if (debugFileRegions.empty()) debugFileSetup(); if (debugFileTrim(file) == debugFileName) { bool found = false; for (auto itr = debugFileRegions.begin(); itr != debugFileRegions.end(); ++itr) { if (itr->first + itr->second > offset) { found = true; TraceEvent("DebugFileTruncate") .detail("Context", context) .detail("Filename", file) .detail("Offset", offset); uint8_t* storeMask = debugFileMask[itr->first]; ASSERT(storeMask); int64_t dbgOffset = std::max((int64_t)0, offset - itr->first); memset(&storeMask[dbgOffset], 0, itr->second - dbgOffset); } } if (!found) TraceEvent("DebugFileSkippingTruncate") .detail("Context", context) .detail("Filename", file) .detail("Offset", offset); } } #else void debugFileCheck(std::string context, std::string file, const void* data, int64_t offset, int length) {} void debugFileSet(std::string context, std::string file, const void* data, int64_t offset, int length) {} void debugFileTruncate(std::string context, std::string file, int64_t offset) {} #endif