1 /** 2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef LIBPANDAFILE_DEBUG_INFO_UPDATER_INL_H_ 17 #define LIBPANDAFILE_DEBUG_INFO_UPDATER_INL_H_ 18 19 #include <type_traits> 20 #include "debug_data_accessor.h" 21 #include "debug_data_accessor-inl.h" 22 #include "debug_helpers.h" 23 #include "line_number_program.h" 24 25 namespace ark::panda_file { 26 27 /** 28 * @brief Handler implementation class for `LineNumberProgramProcessor`. 29 * 30 * Instances of this class traverse debug information of a single binary method 31 * and create all required constant strings (names, types, etc.) in a merged file. 32 */ 33 template <typename T> 34 class LineNumberProgramScrapper final { 35 public: 36 /// @param scrapper object implementing `DebugInfoUpdater`. LineNumberProgramScrapper(T * scrapper,LineProgramState * state)37 explicit LineNumberProgramScrapper(T *scrapper, LineProgramState *state) : scrapper_(scrapper), state_(state) 38 { 39 ASSERT(scrapper_); 40 ASSERT(state_); 41 } 42 43 ~LineNumberProgramScrapper() = default; 44 45 NO_COPY_SEMANTIC(LineNumberProgramScrapper); 46 NO_MOVE_SEMANTIC(LineNumberProgramScrapper); 47 GetState()48 LineProgramState *GetState() const 49 { 50 return state_; 51 } 52 ProcessBegin()53 void ProcessBegin() {} 54 ProcessEnd()55 void ProcessEnd() {} 56 HandleAdvanceLine(int32_t lineDiff)57 bool HandleAdvanceLine([[maybe_unused]] int32_t lineDiff) const 58 { 59 return true; 60 } 61 HandleAdvancePc(uint32_t pcDiff)62 bool HandleAdvancePc([[maybe_unused]] uint32_t pcDiff) const 63 { 64 return true; 65 } 66 HandleSetFile(uint32_t sourceFileId)67 bool HandleSetFile(uint32_t sourceFileId) const 68 { 69 std::string sourceFile = debug_helpers::GetStringFromConstantPool(GetPandaFile(), sourceFileId); 70 scrapper_->GetOrCreateStringItem(sourceFile); 71 return true; 72 } 73 HandleSetSourceCode(uint32_t sourceCodeId)74 bool HandleSetSourceCode(uint32_t sourceCodeId) const 75 { 76 std::string sourceCode = debug_helpers::GetStringFromConstantPool(GetPandaFile(), sourceCodeId); 77 scrapper_->GetOrCreateStringItem(sourceCode); 78 return true; 79 } 80 HandleSetPrologueEnd()81 bool HandleSetPrologueEnd() const 82 { 83 return true; 84 } 85 HandleSetEpilogueBegin()86 bool HandleSetEpilogueBegin() const 87 { 88 return true; 89 } 90 HandleStartLocal(int32_t regNumber,uint32_t nameId,uint32_t typeId)91 bool HandleStartLocal([[maybe_unused]] int32_t regNumber, uint32_t nameId, uint32_t typeId) 92 { 93 std::string name = debug_helpers::GetStringFromConstantPool(GetPandaFile(), nameId); 94 std::string type = debug_helpers::GetStringFromConstantPool(GetPandaFile(), typeId); 95 96 scrapper_->GetOrCreateStringItem(name); 97 scrapper_->GetType(File::EntityId(typeId), type); 98 return true; 99 } 100 HandleStartLocalExtended(int32_t regNumber,uint32_t nameId,uint32_t typeId,uint32_t typeSignatureId)101 bool HandleStartLocalExtended([[maybe_unused]] int32_t regNumber, uint32_t nameId, uint32_t typeId, 102 uint32_t typeSignatureId) 103 { 104 std::string name = debug_helpers::GetStringFromConstantPool(GetPandaFile(), nameId); 105 std::string type = debug_helpers::GetStringFromConstantPool(GetPandaFile(), typeId); 106 std::string typeSign = debug_helpers::GetStringFromConstantPool(GetPandaFile(), typeSignatureId); 107 108 scrapper_->GetOrCreateStringItem(name); 109 scrapper_->GetType(File::EntityId(typeId), type); 110 scrapper_->GetOrCreateStringItem(typeSign); 111 return true; 112 } 113 HandleEndLocal(int32_t regNumber)114 bool HandleEndLocal([[maybe_unused]] int32_t regNumber) 115 { 116 return true; 117 } 118 HandleRestartLocal(int32_t regNumber)119 bool HandleRestartLocal([[maybe_unused]] int32_t regNumber) const 120 { 121 return true; 122 } 123 HandleSetColumn(int32_t columnNumber)124 bool HandleSetColumn([[maybe_unused]] int32_t columnNumber) 125 { 126 return true; 127 } 128 HandleSpecialOpcode(uint32_t pcOffset,int32_t lineOffset)129 bool HandleSpecialOpcode([[maybe_unused]] uint32_t pcOffset, [[maybe_unused]] int32_t lineOffset) 130 { 131 return true; 132 } 133 GetPandaFile()134 const panda_file::File &GetPandaFile() const 135 { 136 return state_->GetPandaFile(); 137 } 138 139 private: 140 T *scrapper_; 141 LineProgramState *state_; 142 }; 143 144 /** 145 * @brief Handler implementation class for `LineNumberProgramProcessor`. 146 * 147 * Instances of this class traverse debug information of a single binary method 148 * and emit the corresponding `LineNumberProgram` into a merged file. 149 */ 150 template <typename T> 151 class LineNumberProgramEmitter final { 152 public: 153 /// @param updater object implementing `DebugInfoUpdater`. LineNumberProgramEmitter(T * updater,LineProgramState * state,LineNumberProgramItemBase * lnpItem,std::vector<uint8_t> * constantPool)154 explicit LineNumberProgramEmitter(T *updater, LineProgramState *state, LineNumberProgramItemBase *lnpItem, 155 std::vector<uint8_t> *constantPool) 156 : updater_(updater), state_(state), lnpItem_(lnpItem), constantPool_(constantPool) 157 { 158 ASSERT(updater_); 159 ASSERT(state_); 160 ASSERT(lnpItem_); 161 ASSERT(constantPool_); 162 } 163 164 ~LineNumberProgramEmitter() = default; 165 166 NO_COPY_SEMANTIC(LineNumberProgramEmitter); 167 NO_MOVE_SEMANTIC(LineNumberProgramEmitter); 168 GetState()169 LineProgramState *GetState() const 170 { 171 return state_; 172 } 173 ProcessBegin()174 void ProcessBegin() {} 175 ProcessEnd()176 void ProcessEnd() 177 { 178 lnpItem_->EmitEnd(); 179 } 180 HandleAdvanceLine(int32_t lineDiff)181 bool HandleAdvanceLine(int32_t lineDiff) const 182 { 183 lnpItem_->EmitAdvanceLine(constantPool_, lineDiff); 184 return true; 185 } 186 HandleAdvancePc(uint32_t pcDiff)187 bool HandleAdvancePc(uint32_t pcDiff) const 188 { 189 lnpItem_->EmitAdvancePc(constantPool_, pcDiff); 190 return true; 191 } 192 HandleSetFile(uint32_t sourceFileId)193 bool HandleSetFile(uint32_t sourceFileId) const 194 { 195 std::string sourceFile = debug_helpers::GetStringFromConstantPool(GetPandaFile(), sourceFileId); 196 auto *sourceFileItem = updater_->GetOrCreateStringItem(sourceFile); 197 lnpItem_->EmitSetFile(constantPool_, sourceFileItem); 198 return true; 199 } 200 HandleSetSourceCode(uint32_t sourceCodeId)201 bool HandleSetSourceCode(uint32_t sourceCodeId) const 202 { 203 std::string sourceCode = debug_helpers::GetStringFromConstantPool(GetPandaFile(), sourceCodeId); 204 auto *sourceCodeItem = updater_->GetOrCreateStringItem(sourceCode); 205 lnpItem_->EmitSetFile(constantPool_, sourceCodeItem); 206 return true; 207 } 208 HandleSetPrologueEnd()209 bool HandleSetPrologueEnd() const 210 { 211 lnpItem_->EmitPrologueEnd(); 212 return true; 213 } 214 HandleSetEpilogueBegin()215 bool HandleSetEpilogueBegin() const 216 { 217 lnpItem_->EmitEpilogueBegin(); 218 return true; 219 } 220 HandleStartLocal(int32_t regNumber,uint32_t nameId,uint32_t typeId)221 bool HandleStartLocal(int32_t regNumber, uint32_t nameId, uint32_t typeId) 222 { 223 std::string name = debug_helpers::GetStringFromConstantPool(GetPandaFile(), nameId); 224 std::string type = debug_helpers::GetStringFromConstantPool(GetPandaFile(), typeId); 225 226 auto *nameItem = updater_->GetOrCreateStringItem(name); 227 auto *typeItem = updater_->GetType(File::EntityId(typeId), type); 228 auto *typeItemName = (typeItem == nullptr) ? updater_->GetOrCreateStringItem(type) : typeItem->GetNameItem(); 229 230 lnpItem_->EmitStartLocal(constantPool_, regNumber, nameItem, typeItemName); 231 return true; 232 } 233 HandleStartLocalExtended(int32_t regNumber,uint32_t nameId,uint32_t typeId,uint32_t typeSignatureId)234 bool HandleStartLocalExtended(int32_t regNumber, uint32_t nameId, uint32_t typeId, uint32_t typeSignatureId) 235 { 236 std::string name = debug_helpers::GetStringFromConstantPool(GetPandaFile(), nameId); 237 std::string type = debug_helpers::GetStringFromConstantPool(GetPandaFile(), typeId); 238 std::string typeSign = debug_helpers::GetStringFromConstantPool(GetPandaFile(), typeSignatureId); 239 240 auto *nameItem = updater_->GetOrCreateStringItem(name); 241 auto *typeItem = updater_->GetType(File::EntityId(typeId), type); 242 auto *typeItemName = (typeItem == nullptr) ? updater_->GetOrCreateStringItem(type) : typeItem->GetNameItem(); 243 auto *typeSignatureItem = updater_->GetOrCreateStringItem(typeSign); 244 245 lnpItem_->EmitStartLocalExtended(constantPool_, regNumber, nameItem, typeItemName, typeSignatureItem); 246 return true; 247 } 248 HandleEndLocal(int32_t regNumber)249 bool HandleEndLocal(int32_t regNumber) 250 { 251 lnpItem_->EmitEndLocal(regNumber); 252 return true; 253 } 254 HandleRestartLocal(int32_t regNumber)255 bool HandleRestartLocal(int32_t regNumber) const 256 { 257 lnpItem_->EmitRestartLocal(regNumber); 258 return true; 259 } 260 HandleSetColumn(int32_t columnNumber)261 bool HandleSetColumn(int32_t columnNumber) 262 { 263 lnpItem_->EmitColumn(constantPool_, 0, columnNumber); 264 return true; 265 } 266 HandleSpecialOpcode(uint32_t pcOffset,int32_t lineOffset)267 bool HandleSpecialOpcode(uint32_t pcOffset, int32_t lineOffset) 268 { 269 lnpItem_->EmitSpecialOpcode(pcOffset, lineOffset); 270 return true; 271 } 272 GetPandaFile()273 const panda_file::File &GetPandaFile() const 274 { 275 return state_->GetPandaFile(); 276 } 277 278 private: 279 T *updater_; 280 LineProgramState *state_; 281 LineNumberProgramItemBase *lnpItem_; 282 std::vector<uint8_t> *constantPool_; 283 }; 284 285 /** 286 * @brief Base class for moving debug information into merged file. 287 * 288 * Type `T` is a CRTP with following methods: 289 * ``` 290 * StringItem *GetOrCreateStringItem(const std::string &s); 291 * BaseClassItem *GetType(File::EntityId type_id, const std::string &type_name) 292 * ``` 293 */ 294 template <typename T> 295 class DebugInfoUpdater { 296 public: DebugInfoUpdater(const File * file)297 explicit DebugInfoUpdater(const File *file) : file_(file) {} 298 299 /** 300 * @brief Collects all required strings from debug information denoted by `debugInfoId`. 301 * 302 * This method must be called for each original method before emitting debug information in a merged file, 303 * so that strings could be reused. 304 */ Scrap(File::EntityId debugInfoId)305 void Scrap(File::EntityId debugInfoId) 306 { 307 DebugInfoDataAccessor debugAcc(*file_, debugInfoId); 308 const uint8_t *program = debugAcc.GetLineNumberProgram(); 309 310 panda_file::LineProgramState state(*file_, File::EntityId(0), debugAcc.GetLineStart(), 311 debugAcc.GetConstantPool()); 312 313 LineNumberProgramScrapper<T> handler(This(), &state); 314 LineNumberProgramProcessor programProcessor(program, &handler); 315 programProcessor.Process(); 316 } 317 318 /** 319 * @brief Emits `LineNumberProgram` in a merged file. 320 * 321 * @param lnpItem target LineNumberProgram. When passing instances of `LineNumberProgramItemBase`, 322 * handler will not emit any `LineNumberProgram` instructions 323 * and will only write their arguments into `constantPool`. 324 * 325 * @param constantPool storage for `LineNumberProgram` instructions' arguments, unique for each emitted method. 326 * 327 * @param debugInfoId method's debug info identifier. 328 */ Emit(LineNumberProgramItemBase * lnpItem,std::vector<uint8_t> * constantPool,File::EntityId debugInfoId)329 void Emit(LineNumberProgramItemBase *lnpItem, std::vector<uint8_t> *constantPool, File::EntityId debugInfoId) 330 { 331 ASSERT(lnpItem); 332 ASSERT(constantPool); 333 334 if (!lnpItem->Empty()) { 335 return; 336 } 337 338 DebugInfoDataAccessor debugAcc(*file_, debugInfoId); 339 const uint8_t *program = debugAcc.GetLineNumberProgram(); 340 341 panda_file::LineProgramState state(*file_, File::EntityId(0), debugAcc.GetLineStart(), 342 debugAcc.GetConstantPool()); 343 344 LineNumberProgramEmitter<T> handler(This(), &state, lnpItem, constantPool); 345 LineNumberProgramProcessor programProcessor(program, &handler); 346 programProcessor.Process(); 347 } 348 349 protected: GetFile()350 const File *GetFile() const 351 { 352 return file_; 353 } 354 355 private: 356 const File *file_; 357 This()358 T *This() 359 { 360 static_assert(std::is_base_of_v<DebugInfoUpdater, T>); 361 return static_cast<T *>(this); 362 } 363 }; 364 } // namespace ark::panda_file 365 366 #endif // LIBPANDAFILE_DEBUG_INFO_UPDATER_INL_H_ 367