Java Code Examples for ghidra.util.Msg#debug()

The following examples show how to use ghidra.util.Msg#debug() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: PackedDatabaseCache.java    From ghidra with Apache License 2.0 6 votes vote down vote up
private boolean isBadDBDir(CachedDB entry) {
	File dbDir = entry.dbDir;
	File[] files = dbDir.listFiles();
	if (files == null) {
		Msg.debug(this, "CachedDB directory not found: " + entry.itemName + ", " + entry.dbDir);
		return true;
	}
	if (files.length == 0) {
		// missing/empty directory indicates not yet opened/unpacked
		entry.lastModifiedTime = 0;
		if (!entry.originalPackedDBExists()) {
			Msg.debug(this, "CachedDB has empty directory and packed file not found: " +
				entry.itemName + ", " + entry.packedDbFilePath);
			return true;
		}
		return false;
	}
	for (File f : files) {
		if (f.getName().endsWith(LocalBufferFile.BUFFER_FILE_EXTENSION) && f.length() != 0) {
			return false;
		}
	}
	Msg.debug(this, "CachedDB is not empty but contains no *.gbf files: " + entry.itemName +
		", " + entry.packedDbFilePath);
	return true;
}
 
Example 2
Source File: AlgorithmSteppingTaskMonitor.java    From ghidra with Apache License 2.0 6 votes vote down vote up
/**
 * Causes this monitor to perform at {@link #wait()}.  Call {@link #step()} to allow the
 * client to continue.
 */
public void pause() {

	if (isCancelled()) {
		return; // no pausing after cancelled
	}

	notifyStepReady();

	synchronized (this) {

		try {
			wait();
		}
		catch (InterruptedException e) {
			Msg.debug(this, "Interrupted waiting for next step", e);
		}

	}
}
 
Example 3
Source File: SplashScreenTest.java    From ghidra with Apache License 2.0 6 votes vote down vote up
private DockingDialog showModalPasswordDialog(Frame parentFrame) throws Exception {
	String dialogTitle = "InfoWindowTest.testSplashScreenPasswordModality() Dialog";
	DialogComponentProvider passwordDialog = runSwing(() -> new PasswordDialog(dialogTitle,
		"Server Type", "Server Name", "Prompt", null, null));

	executeOnSwingWithoutBlocking(
		() -> DockingWindowManager.showDialog(parentFrame, passwordDialog));

	JDialog dialog = waitForJDialog(dialogTitle);
	assertNotNull(dialog);

	Window dialogWindow = SwingUtilities.windowForComponent(dialog);
	Msg.debug(this, "Created modal dialog with parent: " + getTitleForWindow(dialogWindow) +
		" - id: " + System.identityHashCode(dialogWindow));

	return (DockingDialog) dialog;
}
 
Example 4
Source File: AUDataType.java    From ghidra with Apache License 2.0 6 votes vote down vote up
@Override
public int getLength(MemBuffer buf, int maxLength) {

	try {
		if (!checkMagic(buf)) {
			Msg.debug(this, "Invalid AU magic number");
			return -1;
		}
		//Ints always stored as big endian in this format but can be in any kind of program so must get the ints as big endian even when in a little endian program			
		int dataOffset = GhidraBigEndianDataConverter.INSTANCE.getInt(buf, 4);

		int dataSize = GhidraBigEndianDataConverter.INSTANCE.getInt(buf, 8);

		int totalSize = dataOffset + dataSize; //header + data = total size			
		return totalSize;

	}
	catch (Exception e) {
		Msg.debug(this, "Invalid AU data at " + buf.getAddress());
	}
	return -1;
}
 
Example 5
Source File: DecompilerConcurrentQ.java    From ghidra with Apache License 2.0 6 votes vote down vote up
/**
 * Calls dispose on the queue being processed.  Further, the call will block for up to 
 * <tt>timeoutSeconds</tt> while waiting for the queue to finish processing. 
 * 
 * @param timeoutSeconds the number of seconds to wait for the disposed queue to finish
 *        processing
 */
public void dispose(long timeoutSeconds) {
	queue.dispose();

	boolean finished = false;
	try {
		finished = queue.waitUntilDone(timeoutSeconds, TimeUnit.SECONDS);
	}
	catch (InterruptedException e) {
		// we tried!
	}

	if (!finished) {
		Msg.debug(this,
			"Unable to shutdown all tasks in " + timeoutSeconds + " " + TimeUnit.SECONDS);
	}
}
 
Example 6
Source File: VTDomainObjectEventsTest.java    From ghidra with Apache License 2.0 5 votes vote down vote up
@Override
@After
public void tearDown() throws Exception {
	Msg.debug(this, "tearDown()\n");
	db.endTransaction(transactionID, false);
	db.release(VTTestUtils.class);
}
 
Example 7
Source File: GoToPluginTest.java    From ghidra with Apache License 2.0 5 votes vote down vote up
private List<String> search(String text, boolean caseSensitive) {

		QueryData queryData = new QueryData(text, caseSensitive, true);

		GoToQueryResultsTableModel model = runSwing(() -> {
			int maxHits = 100;
			return new GoToQueryResultsTableModel(program, queryData, new ServiceProviderStub(),
				maxHits, TaskMonitor.DUMMY);
		});

		waitForTableModel(model);

		int columnIndex = model.getColumnIndex(LabelTableColumn.class);

		List<String> results = new ArrayList<>();
		List<ProgramLocation> data = model.getModelData();

		if (data.isEmpty()) {
			// debug
			Msg.debug(this, "No search results found *or* failure to wait for threaded table " +
				"model - " + "trying again");
			printOpenWindows();

			waitForTableModel(model);
			data = model.getModelData();

			Msg.debug(this, "\twaited again--still empty? - size: " + data.size());
		}

		for (ProgramLocation loc : data) {
			String label = (String) model.getColumnValueForRow(loc, columnIndex);
			results.add(label);
		}

		return results;
	}
 
Example 8
Source File: SaveState.java    From ghidra with Apache License 2.0 5 votes vote down vote up
/**
 * Gets the boolean value for the given name.
 * @param name the name of the pair.
 * @param defaultValue the default value to be returned if the name does
 * not exist in the map, or it does not contain the proper object type.
 * @return the boolean value associated with the given name or the defaultValue
 * passed in if the name doesn't exist or is the wrong type.
 */
public boolean getBoolean(String name, boolean defaultValue) {
	try {
		Boolean val = (Boolean) map.get(name);
		if (val != null) {
			return val.booleanValue();
		}
	}
	catch (Exception e) {
		Msg.debug(this, "Two different types for option name: " + name);
	}
	return defaultValue;
}
 
Example 9
Source File: GraphComponentTest.java    From ghidra with Apache License 2.0 5 votes vote down vote up
@Test
public void testTwinkleVertex() throws Exception {

	Collection<AbstractTestVertex> vertices = graph.getVertices();
	AbstractTestVertex v = CollectionUtils.any(vertices);

	twinkle(v);

	// TODO debug
	if (v.hasBeenEmphasised()) {

		// below, once we are zoomed-out, then the emphasis should happen
		Msg.debug(this, "No vertice should have been emphasized, since we are zoomed in " +
			" - twinkled vertex: " + v + "; all vertex states: ");
		vertices.forEach(vertex -> {
			Msg.debug(this, vertex + " - " + vertex.hasBeenEmphasised());
		});

		// maybe the graph was scaled??
		Msg.debug(this, "graph scale (should be 1.0): " +
			GraphViewerUtils.getGraphScale(graphComponent.getPrimaryViewer()));
	}

	assertFalse(v.hasBeenEmphasised());

	scaleGraphPastInteractionThreshold();
	twinkle(v);
	assertTrue(v.hasBeenEmphasised());
}
 
Example 10
Source File: AbstractFunctionGraphTest.java    From ghidra with Apache License 2.0 5 votes vote down vote up
protected void debugAction(DockingAction copyAction, ActionContext context) {

		Msg.debug(this, "Copy action not enabled at location " + codeBrowser.getCurrentLocation());

		FGVertex focusedVertex = getFocusedVertex();
		Msg.debug(this, "\tfocused vertex: " + focusedVertex);

		Msg.debug(this, "\tcontext: " + context);

		// Figure out which check in the action failed
		Object clipboardService = getInstanceField("clipboardService", copyAction);
		Msg.debug(this, "\tservice: " + clipboardService);

		Boolean result = (Boolean) invokeInstanceMethod("isValidContext", clipboardService,
			new Class[] { ActionContext.class }, new Object[] { context });
		Msg.debug(this, "\tisValidContext()?: " + result);

		result = (Boolean) invokeInstanceMethod("canCopy", clipboardService);
		Msg.debug(this, "\tcanCopy: " + result);

		Boolean copyFromSelectionEnabled =
			(Boolean) getInstanceField("copyFromSelectionEnabled", clipboardService);
		Msg.debug(this, "\tcopyFromSelectionEnabled: " + copyFromSelectionEnabled);

		String stringContent = (String) getInstanceField("stringContent", clipboardService);
		Msg.debug(this, "\tstringContent: " + stringContent);

		Object location = getInstanceField("currentLocation", clipboardService);
		Msg.debug(this, "\tservice location: " + location);
	}
 
Example 11
Source File: AbstractMergeTest.java    From ghidra with Apache License 2.0 5 votes vote down vote up
protected void waitForMergeCompletion() {
	int totalTime = 0;
	while (mergeMgr != null && !mergeMgr.processingCompleted()) {

		Window win = getWindowByTitleContaining(null, "Merge Information");
		if (win != null) {
			try {
				pressButtonByText(win, "OK");
			}
			catch (AssertionError e) {
				// If we can't press an OK button,then just continue.
			}
		}

		totalTime += sleep();

		if (totalTime >= MAX_MERGE_TIMEOUT) {

			List<Dialog> modals =
				WindowUtilities.getOpenModalDialogsFor(mergeMgr.getMergeTool().getToolFrame());
			Msg.debug(this, "Open modal dialog: ");
			for (Dialog dialog : modals) {
				capture(dialog);
			}

			String trace = createStackTraceForAllThreads();
			Assert.fail("Merge Tool is unexpectedly still not completed after timeout (ms): " +
				MAX_MERGE_TIMEOUT + "\nThread Traces:\n" + trace);
		}
	}
}
 
Example 12
Source File: SwingUpdateManagerTest.java    From ghidra with Apache License 2.0 5 votes vote down vote up
private SwingUpdateManager createUpdateManager(int min, int max) {
	return new SwingUpdateManager(min, max, new Runnable() {

		@Override
		public void run() {
			runnableCalled++;
			Msg.debug(this, "run() called - count: " + runnableCalled);
		}
	});
}
 
Example 13
Source File: ClassPackage.java    From ghidra with Apache License 2.0 5 votes vote down vote up
private void scanSubPackages(TaskMonitor monitor) throws CancelledException {

		File[] subdirs = packageDir.listFiles();
		if (subdirs == null) {
			Msg.debug(this, "Directory does not exist: " + packageDir);
			return;
		}

		for (File subdir : subdirs) {
			monitor.checkCanceled();
			if (!subdir.isDirectory()) {
				continue;
			}

			String pkg = subdir.getName();
			if (pkg.contains(".")) {
				// java can't handle dir names with '.'-- it conflicts with the package structure
				continue;
			}

			if (packageName.length() > 0) {
				pkg = packageName + "." + pkg;
			}

			monitor.setMessage("scanning package: " + pkg);
			children.add(new ClassPackage(rootDir, pkg, monitor));
		}
	}
 
Example 14
Source File: SplashScreenTest.java    From ghidra with Apache License 2.0 5 votes vote down vote up
@After
public void tearDown() {
	Msg.debug(this, "tearDown() - open windows before closing");
	printOpenWindows();

	runSwing(() -> SplashScreen.disposeSplashScreen());

	closeAllWindows();
	printOpenWindows();

	Msg.debug(this, "tearDown() - open windows after closing");
}
 
Example 15
Source File: DmgServerProcessManager.java    From ghidra with Apache License 2.0 4 votes vote down vote up
private void processManagerLoop() {
	while (true) {
		if (cmdCount == 0) {
			Msg.debug(this, "Starting new DMG server process");
		}
		else {
			Msg.debug(this, "Re-starting DMG server process, cmd count: " + cmdCount);
		}
		this.process = createProcess();
		if (process == null) {
			Msg.error(this, "Failed to create new DMG server process, exiting cmd loop");
			return;
		}

		BufferedReader inputReader =
			new BufferedReader(new InputStreamReader(process.getInputStream()));
		PrintWriter outputWriter = new PrintWriter(process.getOutputStream());
		startReaderThread(new BufferedReader(new InputStreamReader(process.getErrorStream())));

		// hack to send the inital "open" to the dmg server process
		Cmd startupCmd = new Cmd("open " + file.getAbsolutePath(), 0);

		try {
			while (++cmdCount % dmgServerMaxCmdPerSession != 0) {
				Cmd cmd = (startupCmd != null) ? startupCmd : cmdQueue.take();
				startupCmd = null;

				synchronized (cmd) {
					try {
						if (cmd.cmdStr != null) {
							watchdog.arm();
							outputWriter.println(cmd.cmdStr);
							outputWriter.flush();

							int expectedResponseCount = cmd.expectedResponseCount;
							if (expectedResponseCount == Cmd.UNKNOWN_RESPONSE_COUNT) {
								expectedResponseCount = readInt(inputReader);
							}
							else if (expectedResponseCount < 0) {
								int nestedResponseCount = -expectedResponseCount;
								expectedResponseCount =
									readInt(inputReader) * nestedResponseCount;
							}
							cmd.results = new ArrayList<>(expectedResponseCount);
							for (int i = 0; i < expectedResponseCount; i++) {
								String s = inputReader.readLine();
								if (s == null) {
									throw new IOException(
										"EOF while reading results from DMG Server");
								}
								cmd.results.add(s);
							}
						}
					}
					catch (IOException ioe) {
						cmd.error = ioe;
						break;// break cmd loop, destroy process and start a new one
					}
					finally {
						watchdog.disarm();
						cmd.notifyAll();
					}
				}
				if (cmd.cmdStr == null) {
					return;// shutdown, exit entire cmd loop
				}
			}
		}
		catch (InterruptedException ie) {
			Msg.error(this, "IntrError", ie);
			return;// shutdown the server processing loop
		}
		finally {
			Msg.info(this, "DMG server process destroyed");
			process.destroy();
			try {
				int exitCode = process.waitFor();
				Msg.debug(this, "DMG Server process exited with: " + exitCode);
			}
			catch (InterruptedException e) {
				// ignore
			}
			process = null;
		}
	}
}
 
Example 16
Source File: ContextState.java    From ghidra with Apache License 2.0 4 votes vote down vote up
public boolean store(int spaceID, Varnode offsetValue, Varnode storedValue, int size) {

		if (locked) {
			throw new IllegalStateException("State is locked");
		}

		AddressSpace addressSpace = addrFactory.getAddressSpace(spaceID);
		if (addressSpace == null) {
			throw new IllegalArgumentException("Unknown spaceID");
		}

		if (storedValue.isConstant() && storedValue.getSize() == 0) {
			// Morph unsized constant
			storedValue =
				new Varnode(addrFactory.getConstantAddress(storedValue.getOffset()), size);
		}
		else if (size != storedValue.getSize()) {
			throw new IllegalArgumentException("storeValue size mismatch");
		}

		cachedLocation = null;
		cachedValue = null;

		if (offsetValue.isConstant()) {
			try {
				Address addr = addressSpace.getAddress(offsetValue.getOffset(), true);
				store(new Varnode(addr, size), storedValue);
				return true;
			}
			catch (AddressOutOfBoundsException e) {
			}
			// TODO: what should we invalidate ?
			if (DEBUG)
				Msg.debug(this, " Store failed: spaceID=" + spaceID + ", offsetValue: " +
					offsetValue.getOffset());
			return false;
		}

		// Handle relative offsets (limited support)
		FrameNode frameNode = getFrameNode(offsetValue, language);
		if (frameNode == null) {
			// TODO: what should we invalidate ?
			if (DEBUG)
				Msg.debug(this, " Store failed: spaceID=" + spaceID + ", offsetValue: " +
					offsetValue.toString(language));
			return false;
		}

		if (debugVarnode == null || frameNode.framePointer.equals(debugVarnode)) {
			if (DEBUG)
				Msg.debug(this, " Store: " + frameNode + " <- " + storedValue.toString(language));
		}

// TODO: Frame reference callback (write) ??

		String frameMapName = getFrameMapName(addressSpace.getName(), frameNode.framePointer);
		HashMap<Long, Varnode> frameMap = getFrameMap(frameMapName, true);
		if (size == 1) {
			frameMap.put(frameNode.frameOffset, storedValue);
		}
		else {
			long baseOffset = frameNode.frameOffset;
			for (int i = 0; i < size; i++) {
				Varnode byteValue = getVarnodeByte(storedValue, i);
				long byteOffset = language.isBigEndian() ? (size - i - 1) : i;
				frameMap.put(baseOffset + byteOffset, byteValue);
			}
		}
		return true;
	}
 
Example 17
Source File: ContextState.java    From ghidra with Apache License 2.0 4 votes vote down vote up
/**
	 * Retrieve the value/operation stored within the specified space using an offset
	 * identified by a value/operation.
	 * @param spaceID
	 * @param offsetValue
	 * @param size
	 * @return stored value/operation or null or DUMMY_BYTE_VARNODE
	 */
	public Varnode get(int spaceID, Varnode offsetValue, int size, TaskMonitor monitor)
			throws CancelledException {
		AddressSpace addressSpace = addrFactory.getAddressSpace(spaceID);
		if (addressSpace == null) {
			throw new IllegalArgumentException("Unknown spaceID: " + spaceID);
		}
		if (spaceID == cachedSpaceId && offsetValue.equals(cachedLocation) &&
			(cachedValue == null || cachedValue.getSize() == size)) {
			return cachedValue;
		}
		cachedSpaceId = spaceID;
		cachedLocation = offsetValue;
		if (offsetValue.isConstant()) {
			try {
				Address addr = addressSpace.getAddress(offsetValue.getOffset());
				Varnode value = get(new Varnode(addr, size), monitor);
				cachedValue = value;
				return value;
			}
			catch (AddressOutOfBoundsException e) {
			}
			if (DEBUG)
				Msg.debug(this,
					" Get failed: spaceID=" + spaceID + ", offsetValue: " + offsetValue.getOffset());
			cachedValue = null;
			return null;
		}

		FrameNode frameNode = getFrameNode(offsetValue, language);
		if (frameNode == null) {
			if (DEBUG)
				Msg.debug(
					this,
					" Get failed: spaceID=" + spaceID + ", offsetValue: " +
						offsetValue.toString(language));
			cachedValue = null;
			return null;
		}

// TODO: Frame reference callback (read) ??

		String frameMapName = getFrameMapName(addressSpace.getName(), frameNode.framePointer);
		Varnode[] bytes = new Varnode[size];
		long baseOffset = frameNode.frameOffset;
		for (int i = 0; i < size; i++) {
			int bytePos = language.isBigEndian() ? (size - i - 1) : i;
			bytes[bytePos] = getByte(frameMapName, baseOffset + i);
			if (bytes[bytePos] == null) {
				// TODO: partial values are unsupported
				if (debugVarnode == null || frameNode.framePointer.equals(debugVarnode)) {
					if (DEBUG)
						Msg.debug(this, " Get failed: " + frameNode + " has unknown bytes");
				}
				cachedValue = null;
				return null;
			}
		}

		Varnode returnVal = combineByteValues(bytes, monitor);
		if (debugVarnode == null || frameNode.framePointer.equals(debugVarnode)) {
			if (DEBUG)
				Msg.debug(this,
					" Get: " + frameNode + " [" + size + "] is " + returnVal.toString(language));
		}
		cachedValue = returnVal;
		return returnVal;
	}
 
Example 18
Source File: ProgramXmlMgr.java    From ghidra with Apache License 2.0 4 votes vote down vote up
/**
 * Returns the program info from the underlying file. T``his method
 * does not make sense to invoke if a write is being performed
 * to a new file.
 * @return the program info
 * @throws SAXException if an XML error occurs
 * @throws IOException if an I/O error occurs
 */
public ProgramInfo getProgramInfo() throws SAXException, IOException {
	if (info != null) {
		return info;
	}

	info = new ProgramInfo();

	XmlPullParser parser =
		XmlPullParserFactory.create(file, new MyErrorHandler(new MessageLog()), false);

	boolean isFileValid = false;

	String ver = parser.getProcessingInstruction(PROGRAM_DTD, "VERSION");
	if (ver != null) {
		try {
			dtdVersion = Integer.parseInt(ver);
		}
		catch (NumberFormatException e) {
			Msg.debug(this, "Unable to parse DTD: " + ver);
		}
	}

	while (parser.hasNext()) {
		XmlElement element = parser.next();
		String name = element.getName();
		if (name.equals("PROGRAM") && element.isStart()) {
			isFileValid = true;

			info.programName = element.getAttribute("NAME");
			info.exePath = element.getAttribute("EXE_PATH");
			info.exeFormat = element.getAttribute("EXE_FORMAT");
			info.imageBase = element.getAttribute("IMAGE_BASE");
		}
		else if (name.equals("INFO_SOURCE") && element.isStart()) {
			info.user = element.getAttribute("USER");
			info.setTool(element.getAttribute("TOOL"));
			info.timestamp = element.getAttribute("TIMESTAMP");
			info.version = element.getAttribute("VERSION");

			if (isOldXml() && info.isGhidra()) {
				info.programName = element.getAttribute("FILE");
			}
		}
		else if (name.equals("PROCESSOR") && element.isStart()) {
			String languageString = element.getAttribute("LANGUAGE_PROVIDER");
			LanguageCompilerSpecPair pair =
				OldLanguageMappingService.processXmlLanguageString(languageString);
			if (pair != null) {
				info.languageID = pair.languageID;
				info.compilerSpecID = pair.compilerSpecID;
			}
			else {
				info.setCompilerSpecID(getForCompilerTag(parser));
			}
			info.processorName = element.getAttribute("NAME");
			info.family = element.getAttribute("FAMILY");
			info.addressModel = element.getAttribute("ADDRESS_MODEL");
			info.endian = element.getAttribute("ENDIAN");

			break;
		}
	}

	parser.dispose();

	if (!isFileValid) {
		info = null;
	}

	return info;
}
 
Example 19
Source File: LongKeyRecordNode.java    From ghidra with Apache License 2.0 4 votes vote down vote up
@Override
public boolean isConsistent(String tableName, TaskMonitor monitor) throws IOException, CancelledException {
	boolean consistent = true;
	long prevKey = 0;
	for (int i = 0; i < keyCount; i++) {
		// Compare each key entry with the previous key
		long key = getKey(i);
		if (i != 0) {
			if (key <= prevKey) {
				consistent = false;
				logConsistencyError(tableName, "key[" + i + "] <= key[" + (i-1) + "]", null);
				Msg.debug(this, "  key[" + i + "].minKey = 0x" + Long.toHexString(key));
				Msg.debug(this, "  key[" + (i-1) + "].minKey = 0x" + Long.toHexString(prevKey));
			}
		}
		prevKey = key;
	}
	
	if ((parent == null || parent.isLeftmostKey(getKey(0))) && getPreviousLeaf() != null) {
		consistent = false;
		logConsistencyError(tableName, "previous-leaf should not exist", null);
	}

	LongKeyRecordNode node = getNextLeaf();
	if (node != null) {
		if (parent == null || parent.isRightmostKey(getKey(0))) {
			consistent = false;
			logConsistencyError(tableName, "next-leaf should not exist", null);
		}
		else {
			LongKeyRecordNode me = node.getPreviousLeaf();
			if (me != this) {
				consistent = false;
				logConsistencyError(tableName, "next-leaf is not linked to this leaf", null);
			}
		}
	}
	else if (parent != null && !parent.isRightmostKey(getKey(0))) {
		consistent = false;
		logConsistencyError(tableName, "this leaf is not linked to next-leaf", null);
	}
	
	return consistent;
}
 
Example 20
Source File: TransientProjectManager.java    From ghidra with Apache License 2.0 4 votes vote down vote up
/**
 * Get the transient project associated with a specific Ghidra protocol 
 * connector.  This method will establish a connection if needed.
 * @param protocolConnector Ghidra protocol connector
 * @param readOnly true if project data should be treated as read-only
 * @return transient project data
 * @throws IOException
 */
synchronized TransientProjectData getTransientProject(GhidraProtocolConnector protocolConnector,
		boolean readOnly) throws IOException {

	TransientProjectData projectData;

	// try to avoid excessive accumulation of unreferenced transient project data.
	// It is assumed that calls to this method are generally infrequent and may be slow
	System.gc();

	String repoName = protocolConnector.getRepositoryName();
	if (repoName == null) {
		throw new IllegalArgumentException(
			"specified protocol connector does not correspond to a repository");
	}

	RepositoryInfo repositoryInfo =
		new RepositoryInfo(protocolConnector.getRepositoryRootGhidraURL(), repoName, readOnly);

	projectData = repositoryMap.get(repositoryInfo);

	if (projectData == null || !projectData.stopCleanupTimer()) { // cleanup suspended

		if (protocolConnector.connect(readOnly) != GhidraURLConnection.GHIDRA_OK) {
			return null;
		}

		RepositoryAdapter repositoryAdapter = protocolConnector.getRepositoryAdapter();
		if (repositoryAdapter == null || !repositoryAdapter.isConnected()) {
			throw new NotConnectedException("protocol connector not connected to repository");
		}

		projectData = createTransientProject(repositoryAdapter, repositoryInfo);
		if (projectData != null) {
			repositoryMap.put(repositoryInfo, projectData);
		}
	}
	else {

		Msg.debug(this, "Reusing existing TransientProjectData: " + projectData.repositoryInfo);

		try {
			RepositoryAdapter repository = projectData.getRepository();
			repository.connect();
			protocolConnector.connect(repository);
		}
		finally {
			projectData.startCleanupTimer(); // resume cleanup timer
		}

	}

	Msg.debug(this, "Number of active TransientProjectData instances: " + repositoryMap.size());

	return projectData;
}