public void applyTo(MessageLog log) throws IOException, PdbException, CancelledException {
		if (!parsed) {
			throw new IOException("PDB: parse() must be called before applyTo()");
		}

		checkPdbLoaded();

		errHandler.setMessageLog(log);
		Msg.debug(this, "Found PDB for " + program.getName() + ": " + pdbFile);
		try {

			ApplyDataTypes applyDataTypes = null;
			ApplyTypeDefs applyTypeDefs = null;

			boolean typesFlushed = false;

			while (parser.hasNext()) {
				if (hasErrors()) {
					throw new IOException(getErrorAndWarningMessages());
				}
				monitor.checkCancelled();
				XmlElement element = parser.next();
				if (!element.isStart()) {
					continue;
				}
//				long start = System.currentTimeMillis();
				if (element.getName().equals("pdb")) {
					/*
					String exe = element.getAttribute("exe");
					exe = (exe == null ? "" : exe.toLowerCase());
					File exeFile = new File(program.getExecutablePath());
					if (!exeFile.getName().toLowerCase().startsWith(exe)) {
						throw new RuntimeException("'"+pdbFile.getName()+"' not valid for '"+exeFile.getName()+"'");
					}
					*/
				}
				else if (element.getName().equals("enums")) {
					// apply enums - no data type dependencies
					ApplyEnums.applyTo(parser, this, monitor, log);
				}
				else if (element.getName().equals("datatypes")) {
					if (applyDataTypes == null) {
						applyDataTypes = new ApplyDataTypes(this, log);
					}
					applyDataTypes.preProcessDataTypeList(parser, false, monitor);
				}
				else if (element.getName().equals("classes")) {
					if (applyDataTypes == null) {
						applyDataTypes = new ApplyDataTypes(this, log);
					}
					applyDataTypes.preProcessDataTypeList(parser, true, monitor);
				}
				else if (element.getName().equals("typedefs")) {
					applyTypeDefs = new ApplyTypeDefs(this, parser, monitor, log);
				}
				else if (element.getName().equals("functions")) {
					// apply functions (must occur within XML after all type sections)
					if (!typesFlushed) {
						completeDefferedTypeParsing(applyDataTypes, applyTypeDefs, log);
						typesFlushed = true;
					}
					ApplyFunctions.applyTo(this, parser, monitor, log);
				}
				else if (element.getName().equals("tables")) {
					// apply tables (must occur within XML after all other sections)
					if (!typesFlushed) {
						completeDefferedTypeParsing(applyDataTypes, applyTypeDefs, log);
						typesFlushed = true;
					}
					ApplyTables.applyTo(this, parser, monitor, log);
				}
//				Msg.debug(this,
//					element.getName().toUpperCase() + ": " + (System.currentTimeMillis() - start) +
//						" ms");
			}

			if (!typesFlushed) {
				completeDefferedTypeParsing(applyDataTypes, applyTypeDefs, log);
			}

			Options options = program.getOptions(Program.PROGRAM_INFO);
			options.setBoolean(PdbParserConstants.PDB_LOADED, true);

			if (dataTypeParser != null && dataTypeParser.hasMissingBitOffsetError()) {
				log.appendMsg("PDB",
					"One or more bitfields were specified without bit-offset data.\nThe use of old pdb.xml data could be the cause.");
			}
		}
		catch (CancelledException e) {
			throw e;
		}
		catch (Exception e) {
			// Exception could occur if a symbol element is missing an important attribute such
			// as address or length
			String message = e.getMessage();
			if (message == null) {
				message = e.getClass().getSimpleName();
			}
			message = "Problem parsing or applying PDB information: " + message;

			Msg.error(this, message, e);
			throw new IOException(message, e);
		}
		finally {
			cleanup();
		}
		if (hasErrors()) {
			throw new IOException(getErrorAndWarningMessages());
		}
	}
Apply PDB debug information to the current program

@param log  MessageLog used to record errors
@throws IOException  if an error occurs during parsing
@throws PdbException  if PDB file has already been loaded
@throws CancelledException  if user cancels the current action
Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbParser.java