From a0f6b67dc41938e255b626a5f342fa1f6455fcf4 Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Mon, 4 Apr 2022 17:05:29 -0400 Subject: [PATCH] GP-1821: Reword breakpoint states, toggles, and icons --- Ghidra/Debug/Debugger/certification.manifest | 43 +- .../DebuggerBreakpointMarkerPlugin.html | 57 +- .../images/DebuggerBreakpointMarkerPlugin.png | Bin 64007 -> 64900 bytes .../images/breakpoint-clear.png | Bin 365 -> 665 bytes .../images/breakpoint-disable.png | Bin 295 -> 684 bytes .../images/breakpoint-enable.png | Bin 269 -> 561 bytes .../DebuggerBreakpointsPlugin.html | 129 ++-- .../images/DebuggerBreakpointsPlugin.png | Bin 28254 -> 27939 bytes .../images/breakpoint-clear.png | Bin 0 -> 665 bytes .../images/breakpoint-disable.png | Bin 295 -> 684 bytes .../images/breakpoint-enable-ineff.png | Bin 0 -> 513 bytes .../images/breakpoint-enable.png | Bin 269 -> 561 bytes .../images/breakpoint-ineffective-d.png | Bin 353 -> 0 bytes .../images/breakpoint-ineffective-e.png | Bin 302 -> 0 bytes .../images/breakpoint-mixed-de.png | Bin 368 -> 0 bytes .../images/breakpoint-mixed-ed.png | Bin 373 -> 0 bytes .../images/breakpoint-mixed.png | Bin 0 -> 681 bytes .../breakpoint-overlay-inconsistent.png | Bin 0 -> 305 bytes .../images/breakpoints-clear-all.png | Bin 504 -> 732 bytes .../images/breakpoints-disable-all.png | Bin 420 -> 732 bytes .../images/breakpoints-enable-all.png | Bin 329 -> 626 bytes .../images/breakpoints-make-effective.png | Bin 381 -> 402 bytes .../core/debug/gui/DebuggerResources.java | 116 ++-- .../gui/breakpoint/BreakpointLocationRow.java | 24 +- ...BreakpointEnablementTableCellRenderer.java | 72 --- ...erBreakpointLocEnabledTableCellEditor.java | 67 -- ...uggerBreakpointLocationsActionContext.java | 11 +- .../DebuggerBreakpointMarkerPlugin.java | 382 +++++------- ...buggerBreakpointStateTableCellEditor.java} | 23 +- ...ggerBreakpointStateTableCellRenderer.java} | 25 +- .../DebuggerBreakpointsProvider.java | 115 ++-- ...buggerLogicalBreakpointsActionContext.java | 13 +- .../gui/breakpoint/LogicalBreakpointRow.java | 23 +- .../gui/objects/components/ObjectTree.java | 2 +- ...ebuggerLogicalBreakpointServicePlugin.java | 134 ++-- .../breakpoint/LogicalBreakpointInternal.java | 39 +- .../breakpoint/LoneLogicalBreakpoint.java | 27 +- .../breakpoint/MappedLogicalBreakpoint.java | 213 +++++-- .../DebuggerLogicalBreakpointService.java | 63 +- .../app/services/LogicalBreakpoint.java | 589 ++++++++++-------- .../resources/images/alt-breakpoint-clear.png | Bin 1449 -> 0 bytes .../images/alt-breakpoint-disable.png | Bin 1355 -> 0 bytes .../images/alt-breakpoint-enable.png | Bin 1307 -> 0 bytes .../images/alt-breakpoint-ineffective-d.png | Bin 353 -> 0 bytes .../images/alt-breakpoint-ineffective-e.png | Bin 302 -> 0 bytes .../images/alt-breakpoint-mixed-de.png | Bin 1439 -> 0 bytes .../images/alt-breakpoint-mixed-ed.png | Bin 1440 -> 0 bytes .../resources/images/alt-breakpoint-set.png | Bin 1417 -> 0 bytes .../images/alt-breakpoints-clear-all.png | Bin 1699 -> 0 bytes .../images/alt-breakpoints-disable-all.png | Bin 1509 -> 0 bytes .../images/alt-breakpoints-enable-all.png | Bin 1406 -> 0 bytes .../images/alt-breakpoints-make-effective.png | Bin 1441 -> 0 bytes .../main/resources/images/alt-breakpoints.png | Bin 1445 -> 0 bytes .../resources/images/breakpoint-clear.png | Bin 365 -> 665 bytes .../images/breakpoint-disable-ineff.png | Bin 0 -> 614 bytes .../resources/images/breakpoint-disable.png | Bin 295 -> 684 bytes .../images/breakpoint-enable-ineff.png | Bin 0 -> 513 bytes .../resources/images/breakpoint-enable.png | Bin 269 -> 561 bytes .../images/breakpoint-ineffective-d.png | Bin 353 -> 0 bytes .../images/breakpoint-ineffective-e.png | Bin 302 -> 0 bytes .../resources/images/breakpoint-mixed-de.png | Bin 368 -> 0 bytes .../resources/images/breakpoint-mixed-ed.png | Bin 373 -> 0 bytes .../images/breakpoint-mixed-ineff.png | Bin 0 -> 632 bytes .../resources/images/breakpoint-mixed.png | Bin 0 -> 681 bytes .../breakpoint-overlay-inconsistent.png | Bin 0 -> 305 bytes .../main/resources/images/breakpoint-set.png | Bin 383 -> 0 bytes .../images/breakpoints-clear-all.png | Bin 504 -> 732 bytes .../images/breakpoints-disable-all.png | Bin 420 -> 732 bytes .../images/breakpoints-enable-all.png | Bin 329 -> 626 bytes .../images/breakpoints-make-effective.png | Bin 381 -> 402 bytes .../src/main/resources/images/breakpoints.png | Bin 356 -> 0 bytes .../src/main/svg/breakpoint-clear.svg | 27 +- ...t-set.svg => breakpoint-disable-ineff.svg} | 25 +- .../src/main/svg/breakpoint-disable.svg | 27 +- ...tive-e.svg => breakpoint-enable-ineff.svg} | 27 +- .../src/main/svg/breakpoint-enable.svg | 29 +- .../src/main/svg/breakpoint-mixed-ed.svg | 42 -- ...ctive-d.svg => breakpoint-mixed-ineff.svg} | 17 +- ...oint-mixed-de.svg => breakpoint-mixed.svg} | 25 +- .../svg/breakpoint-overlay-inconsistent.svg | 46 ++ .../src/main/svg/breakpoints-clear-all.svg | 46 +- .../src/main/svg/breakpoints-disable-all.svg | 30 +- .../src/main/svg/breakpoints-enable-all.svg | 33 +- .../main/svg/breakpoints-make-effective.svg | 44 +- .../Debugger/src/main/svg/breakpoints.svg | 40 -- ...ggerBreakpointMarkerPluginScreenShots.java | 40 ++ .../DebuggerBreakpointsPluginScreenShots.java | 12 +- .../DebuggerBreakpointMarkerPluginTest.java | 192 +++--- .../DebuggerBreakpointsProviderTest.java | 46 +- .../DebuggerLogicalBreakpointServiceTest.java | 212 +++++-- .../app/plugin/core/marker/MarkerManager.java | 6 +- .../plugin/core/marker/PointMarkerSet.java | 11 +- .../ghidra/app/services/MarkerService.java | 6 +- 93 files changed, 1683 insertions(+), 1467 deletions(-) create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-clear.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-enable-ineff.png delete mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-ineffective-d.png delete mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-ineffective-e.png delete mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed-de.png delete mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed-ed.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed.png create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-overlay-inconsistent.png delete mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointEnablementTableCellRenderer.java delete mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocEnabledTableCellEditor.java rename Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/{DebuggerBreakpointEnablementTableCellEditor.java => DebuggerBreakpointStateTableCellEditor.java} (75%) rename Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/{DebuggerBreakpointLocEnabledTableCellRenderer.java => DebuggerBreakpointStateTableCellRenderer.java} (61%) delete mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-clear.png delete mode 100755 Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-disable.png delete mode 100755 Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-enable.png delete mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-ineffective-d.png delete mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-ineffective-e.png delete mode 100755 Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-mixed-de.png delete mode 100755 Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-mixed-ed.png delete mode 100755 Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-set.png delete mode 100755 Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-clear-all.png delete mode 100755 Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-disable-all.png delete mode 100755 Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-enable-all.png delete mode 100755 Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-make-effective.png delete mode 100755 Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-disable-ineff.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-enable-ineff.png delete mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-ineffective-d.png delete mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-ineffective-e.png delete mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed-de.png delete mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed-ed.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed-ineff.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed.png create mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-overlay-inconsistent.png delete mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-set.png delete mode 100644 Ghidra/Debug/Debugger/src/main/resources/images/breakpoints.png rename Ghidra/Debug/Debugger/src/main/svg/{breakpoint-set.svg => breakpoint-disable-ineff.svg} (55%) rename Ghidra/Debug/Debugger/src/main/svg/{breakpoint-ineffective-e.svg => breakpoint-enable-ineff.svg} (55%) delete mode 100644 Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-ed.svg rename Ghidra/Debug/Debugger/src/main/svg/{breakpoint-ineffective-d.svg => breakpoint-mixed-ineff.svg} (56%) rename Ghidra/Debug/Debugger/src/main/svg/{breakpoint-mixed-de.svg => breakpoint-mixed.svg} (53%) create mode 100644 Ghidra/Debug/Debugger/src/main/svg/breakpoint-overlay-inconsistent.svg delete mode 100644 Ghidra/Debug/Debugger/src/main/svg/breakpoints.svg diff --git a/Ghidra/Debug/Debugger/certification.manifest b/Ghidra/Debug/Debugger/certification.manifest index c6c0360437..2e722617b4 100644 --- a/Ghidra/Debug/Debugger/certification.manifest +++ b/Ghidra/Debug/Debugger/certification.manifest @@ -29,12 +29,12 @@ src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-disab src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-enable.png||GHIDRA||||END| src/main/help/help/topics/DebuggerBreakpointsPlugin/DebuggerBreakpointsPlugin.html||GHIDRA||||END| src/main/help/help/topics/DebuggerBreakpointsPlugin/images/DebuggerBreakpointsPlugin.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-clear.png||GHIDRA||||END| src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-disable.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-enable-ineff.png||GHIDRA||||END| src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-enable.png||GHIDRA||||END| -src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-ineffective-d.png||GHIDRA||||END| -src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-ineffective-e.png||GHIDRA||||END| -src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed-de.png||GHIDRA||||END| -src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed-ed.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed.png||GHIDRA||||END| +src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-overlay-inconsistent.png||GHIDRA||||END| src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-clear-all.png||GHIDRA||||END| src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-disable-all.png||GHIDRA||||END| src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-enable-all.png||GHIDRA||||END| @@ -132,35 +132,21 @@ src/main/help/help/topics/DebuggerWatchesPlugin/DebuggerWatchesPlugin.html||GHID src/main/help/help/topics/DebuggerWatchesPlugin/images/DebuggerWatchesPlugin.png||GHIDRA||||END| src/main/resources/defaultTools/Debugger.tool||GHIDRA||||END| src/main/resources/images/add.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| -src/main/resources/images/alt-breakpoint-clear.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoint-disable.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoint-enable.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoint-ineffective-d.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoint-ineffective-e.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoint-mixed-de.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoint-mixed-ed.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoint-set.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoints-clear-all.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoints-disable-all.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoints-enable-all.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoints-make-effective.png||GHIDRA||||END| -src/main/resources/images/alt-breakpoints.png||GHIDRA||||END| src/main/resources/images/attach.png||GHIDRA||||END| src/main/resources/images/autoread.png||GHIDRA||||END| src/main/resources/images/blank.png||GHIDRA||||END| src/main/resources/images/breakpoint-clear.png||GHIDRA||||END| +src/main/resources/images/breakpoint-disable-ineff.png||GHIDRA||||END| src/main/resources/images/breakpoint-disable.png||GHIDRA||||END| +src/main/resources/images/breakpoint-enable-ineff.png||GHIDRA||||END| src/main/resources/images/breakpoint-enable.png||GHIDRA||||END| -src/main/resources/images/breakpoint-ineffective-d.png||GHIDRA||||END| -src/main/resources/images/breakpoint-ineffective-e.png||GHIDRA||||END| -src/main/resources/images/breakpoint-mixed-de.png||GHIDRA||||END| -src/main/resources/images/breakpoint-mixed-ed.png||GHIDRA||||END| -src/main/resources/images/breakpoint-set.png||GHIDRA||||END| +src/main/resources/images/breakpoint-mixed-ineff.png||GHIDRA||||END| +src/main/resources/images/breakpoint-mixed.png||GHIDRA||||END| +src/main/resources/images/breakpoint-overlay-inconsistent.png||GHIDRA||||END| src/main/resources/images/breakpoints-clear-all.png||GHIDRA||||END| src/main/resources/images/breakpoints-disable-all.png||GHIDRA||||END| src/main/resources/images/breakpoints-enable-all.png||GHIDRA||||END| src/main/resources/images/breakpoints-make-effective.png||GHIDRA||||END| -src/main/resources/images/breakpoints.png||GHIDRA||||END| src/main/resources/images/closedFolder.png||Modified Nuvola Icons - LGPL 2.1||||END| src/main/resources/images/conf.png||GHIDRA||||END| src/main/resources/images/connect.png||GHIDRA||||END| @@ -204,18 +190,17 @@ src/main/resources/images/write-trace.png||Tango Icons - Public Domain||||END| src/main/svg/attach.svg||GHIDRA||||END| src/main/svg/blank.svg||GHIDRA||||END| src/main/svg/breakpoint-clear.svg||GHIDRA||||END| +src/main/svg/breakpoint-disable-ineff.svg||GHIDRA||||END| src/main/svg/breakpoint-disable.svg||GHIDRA||||END| +src/main/svg/breakpoint-enable-ineff.svg||GHIDRA||||END| src/main/svg/breakpoint-enable.svg||GHIDRA||||END| -src/main/svg/breakpoint-ineffective-d.svg||GHIDRA||||END| -src/main/svg/breakpoint-ineffective-e.svg||GHIDRA||||END| -src/main/svg/breakpoint-mixed-de.svg||GHIDRA||||END| -src/main/svg/breakpoint-mixed-ed.svg||GHIDRA||||END| -src/main/svg/breakpoint-set.svg||GHIDRA||||END| +src/main/svg/breakpoint-mixed-ineff.svg||GHIDRA||||END| +src/main/svg/breakpoint-mixed.svg||GHIDRA||||END| +src/main/svg/breakpoint-overlay-inconsistent.svg||GHIDRA||||END| src/main/svg/breakpoints-clear-all.svg||GHIDRA||||END| src/main/svg/breakpoints-disable-all.svg||GHIDRA||||END| src/main/svg/breakpoints-enable-all.svg||GHIDRA||||END| src/main/svg/breakpoints-make-effective.svg||GHIDRA||||END| -src/main/svg/breakpoints.svg||GHIDRA||||END| src/main/svg/connect.svg||GHIDRA||||END| src/main/svg/console.svg||GHIDRA||||END| src/main/svg/continue.svg||GHIDRA||||END| diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html index 8c45dd570b..d79889ac73 100644 --- a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html @@ -24,38 +24,41 @@

For a description of how breakpoints are managed logically in Ghidra, please read about the Breakpoints - window. Each individual breakpoint location is placed in its respective listing. By default, - disabled breakpoints are not readily visible in the dynamic listing. Breakpoints are best - controlled using the static program listing, where they are stored as bookmarks. When this - plugin is active, additional actions are available for managing those bookmarks and their - mapped breakpoints on target, when applicable. The actions in the listing manipulate the - logical breakpoint as a whole, not just the individual location. Furthermore, depending on the - target, locations' states may be bound to others' via a common breakpoint specification.

+ window. Each individual breakpoint location is placed in its respective listing. Breakpoints + are best controlled using the static program listing, where they are stored as bookmarks. When + this plugin is active, additional actions are available for managing breakpoints and their + locations on target. The actions in the static listing manipulate the logical breakpoint as a + whole; whereas, the actions in the dynamic listing tend to manipulate just the locations for + the current target. NOTE: Depending on the connected debugger, locations resulting from + a common specification may not be independently manipulated.

Actions

-

The following actions are added to all listings by the breakpoint marker plugin. They allow - the placement and toggling of breakpoints by address, kind, and length. To set breakpoints on - arbitrary expressions, use the The following actions are added to all disassembly listings by the breakpoint marker plugin. + They allow the placement and toggling of breakpoints by address, kind, and length. To set + breakpoints on arbitrary expressions, use the Set - Breakpoint action of the Commands and Objects window.

+ Breakpoint action of the Objects window. NOTE: These actions may also appear in + other address-based contexts, e.g., the decompiler listing; however, those contexts often lack + any indication of breakpoint presence or state.

-

Toggle +

Toggle Breakpoint (K)

This action is always available, and it is suitable for almost all cases. If there is a breakpoint at the cursor, this simply toggles its state. If there is no breakpoint at the - cursor, this will prompt to set one, giving a reasonable set of default parameters based on the - context at the cursor. At an instruction, it will prefer to set a Software Execution - breakpoint. At defined data, it will prefer to set a Read/Write breakpoint of the size of data. - At undefined data, or if the target does not support the suggested default, the default kind is - left unselected. Please use one of the Set Breakpoint actions to force specific commands. - Please beware: the default parameters are not always acceptable to the connected debugger.

+ cursor, this will behave like Set Breakpoint, giving a reasonable set of default + parameters based on the context at the cursor. At an instruction, it will prefer to set a + Software Execution breakpoint. At defined data, it will prefer to set a Read/Write breakpoint + of the size of data. At undefined data, or if the target does not support the suggested + default, the default kind is left unselected. Please use one of the Set Breakpoint + actions to force specific commands. NOTE: The default parameters are not guaranteed to + be accepted by the connected debugger.

Set Breakpoint

-

This action is available on the dynamic listing when the target supports at least one +

This menu is available on the dynamic listing when the target supports at least one breakpoint kind. This menu is always available on the static listing. It displays set breakpoint actions for each reasonable combination of kinds supported by the target. In the static listing, all reasonable combinations are available, regardless of target support; @@ -74,19 +77,19 @@

Enable @@ -111,7 +114,7 @@

The background coloring of enabled and disabled, effective and ineffective breakpoints can be configured in the tool's options. By default, enabled breakpoints are colored a desaturated - red, ineffective breakpoints are colored grey, and disabled breakpoints have no background at + blue, ineffective breakpoints are colored grey, and disabled breakpoints have no background at all.

diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/DebuggerBreakpointMarkerPlugin.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/DebuggerBreakpointMarkerPlugin.png index 99a16b7d9ff96cb1b0bd32d1dcd57da452f157c0..871b2c670e88df0c0e37d82fe2c03824ca266cbe 100644 GIT binary patch literal 64900 zcmbrl2{@E*8$L{mq!KF0R(6RAm8??~iBLp{ku`hv-5@2|vJTlJ`;vV(w(LvRAKQUJvU7@$ zA3i4|qX5Xr$k$F&g72ul>)jwD^C44w_&~!|e>IWH^P&j}pj*dFJ|nNma{bhn$mULx z&besL=66xx2H%ZOhN+Lmx9)GM-8biW^vvBh74`XX$hEr2(T^W2K>@@{{wwKC4>?J7LEaH%6#+wH}`Tb=t?@19XkLv_a$Z2!Xg zy4BekTt!}wqx&p2b+$X+Hcphm_D)9h6^2 z;?dZf4AHjaJ?QuOX7QDfpHX28vvLk!`hF;kl*R@vNNjtWi_;2ro8O@o%qO_Y=)?`W z%gEf#?C~t!<)zObKNx6Da1MWC*$d~Q`PtjfTTg6D*rnMFgg)D!SBL##CKN)FuP#q_ z!6hG>eM^I|274|jDR3rw4-mQ&dg~oUa8-~*C~T<DaPl_LA)`S;&5jr1-<#|pT@z<}%MYBcdYcHFpR~W~sKopbn zc49mkVr9@RDXQo^Pfo&J0Wa<&F6iO5HPEmVdA9utK;G34H!z^|WQ_HV*x(j$tdBf< zrh5;1()4E-^5i^K$8-bnUQ7Fv#74Oiv-a-|XIblSTx*tpDPn)=!(oYzSxZ(!FpG6Z z?C_O4rcdw7hn?$=wOh#h5xZq6Jd!|dDct)d&{EiYwmMH)CxCiESwA3gIkOUTC3rIs`z%D)@0hj9D~pqV-3eC8g5vNA*Nwv-}nwpO_O)!WT zlAhX$(a{liw6(dl1;(;Vbc%&;S#n`Zik-crMr=-ZSBLm&L&mtqzLh*E7Qe+@anf{f zWRiUB=Gf&}!6JEj$MV)wrt$Nrh87#uvmalm6bxT!a^mNF8PJOols0N zPL+`P&k*2lZ^?Ej$&b9!WTMAzb2bkfa#Au(fHg^BsVZUd$-h7N=ih0C5Ft9nvUO~x z*{JqzD}wn}p9Qq0WSE-U5@q`UARU!p4mXf}<pdjp2{|VI9GC%AmNmAu!=g7i^@|^rgJya zUerB->vB;w8HZJF*%!=w_;V8fzPG}w1ja3>$L>v`SCtn&i<KKErATe&Z{q3wb|~yO%V!;6?u9TlGm<@R`Lsse?i*DbY&V%_F4)>6XT0y zoM-L30O`GnchR|;lq?QYwN;gSQywMOSZ@7ZxrfjzxIGtQ3qRMlbk5fs4+M$JffS#U zrh}x)*C7+G;;6TCiX;HPe_x|uCy~B`{0RruEOns!tAbgwD0Qfk7U}Z0+$@dtAl{sv zHVXLK{eOi!cmF?@%Ox6aKEpUB;dO9ZCE=cLaPZmm^zY!C^w7E!j(KmC5Gx z^(&&LXIsPA!&2n}EM^dG^x82_X$oPjm3DJGJ>-^N5O6u1aS(qGiiU>jsmI^9Z?)Yx z9BET|MNI%!uxJ5~sm&e|B5&i?z-(%VOSJc>`B4lAmtH2S^^C{hT76W6_T zcE!#Y`krgvcyzFCsZ4^}^fMpstu;?cdkn=7Sl~^|t`2_vDs0lv5v;e#@Bxsbkn>Io ze%8f#%e&u|nAQ#5w4Ym#0&?$@PRWr@{r8T9VxVH}$kLslRd%TNE1KATifJNYdj)@6 zu7cENgYe=p9uLq~PC{I7|J}j^ciK*~ngm!R%#a()+hIWLI9In5muF z7GBnD1zg3FR%bC%tM-R$6#^CR%3f^V`d;U~UU=1e=vEES@#&PlDYvd`>2kXw2yh&A zAs}s^E(th#6EKWx@Y=h~Q~+uTQ6(4%waH3u+Zf(ur7R&!mEq z_nQ)e173e(e{F{;F*C`FBHFUOlqWrRvouSjbD)#*UIy#gT;&A^(~|eHh&13j zxQ4?UZvai~*MY4qh0OzO2@wa0@LnC&=3Y-D;(&#ChaG1QlAjiVadjS6Nsj|Mc!PG_ zlBGS!si~P(-VsEQ#CUP%)nIquRV{k)W(Y8XyCkqXSB5fu4xaTf*?;Y(-yyaQy$)~? z>?x*)vegeL8y0%v=`LM*A1$Djv$v*yKzX0ff&Al7rQG^<;3FL;QP}3>6~a~^0}5{(blj=dgY0PdDvU}yEXp=_+!KPb!?cRE4Oa7lBjHLl{cX&CgxEToQ%tHWZl zQBJWM17F+_1awuK*L<>bG9Y=G{;|&Z{Nw|1%k(S>#APVr1!5K9%iN4vS4mG!38Oat8sl>;;@n z>~wUgH6$f$A@TOC0Q&-g^8PJ5he&v%;;%g6?*(G?t5V%nI`hCx0J~$A3SOEXT3#|sc{1a7hR0n9Uuo8Bu?1F#B|(Vo zTRNmi2O*c?oC7$d&5^v-1WPWdz)KLw-a0DcEIxT&{{aCps#rfMQR5l|a4W^+BwlK0 ze5WXJ6maDK@kceFgw&ZfV5&eE~x# z5xU)Noc`@7!`M4L2B`Pa$`$*j?77`z;l;8{{yB*hPv6}!rbBw)o7}df=_IZiKBvXp zdsdP3XcNv-OCE4Ov0-b@OxR4NHpWtnP^e>_;2s$?xEPbnqu*E0RHRz=Qpw0B01#(s3UN$rjrtvF~nNa2>CKhqT% zJa_s7T&rdTmVV|_ReKN8Cc7M7?a}cJUW8c(BlNwPoK~dYiI^y5_lW^j={Va`qOR`I z;NajeTKmR%wqIc_byVMtQ2MYgAU5;6*A&do;0UgVLEl`oiCD+H%0ILp&!khup}!Ke z4t|swchIv)B;<*@jNP!3iS;$_5_^3Rksn*4Y_e>66UGqmNp(b>J)LUdF-iToN~%fM z8^NhQDF)vv2N&^jB8@2lR{1uJ{)eJYCzW@_dFH!Z1Psu|E;hZGyvI*SNGUP=L9W+! z0baNIjKpp4koqWy56qN3>KVjOkAimJ5NwB6c)hHQx{(IQTWL3kvF{@Hs1e?+qh2-~ z@ID$b==mk*y7eP_#hv$>h<$~PiAy9v$0=r%pR?CQqpp!*cy!S0o_HMypiNd2kr<97 zGJ}V|B4e}@*^h%@K+uA`^Iyni;UxyfQ35FCe(xW!6=U>yxT3RX_HZ7SKJ}UOy7|NM zOANY_{MPHHlXd#DPA3`+W*1(NPC@9+4D@WdM%`jLPi*+B6TU?O@F5yZ$&U=;n2?W ztv;T@cB0-1j6reGjRp(6d%qwsemH{~dqP50cOhGH(>=tsu%wAy^Oi_rh1D2ESB&5z z5)nnHIfCl1aZBmUJ(beIuqu<>Xh_;sbZogtlvl-U-<^4pJKY>3?Z5>LL`;@p&u~Y~ zNjn`fcH>~*I`6?Nfal*7a>N3+y$OgBZ#tW_aloNZb=avX8YrwkI7M@Lbp)53;Q6Fu z!T3mh9(v;j`D9Ju=^KD5>dX8F4pxPG>6FBHVjgO*~;Nf>~IxgM_ZAT&rJF>pDO2kG89X)0BP!t0{9-fq58j+LP5SJE=- z*}x^!?+F^3NqzJ;*>H=2LqM25f@CigBa`X?QMYGy8ec=X&tf zvPWG=;;S?^h;BJ#I+x_H#1GgT%@Y_iK1j_Z>vkc}!j^_Txc{@yIc^bvfDWYgu1O3W zE5UUBni!c%lGL&`>D-JMye|27w1fhP@kZ^)6olAq+N)`yzD_WCzr?oaP_UYI!H2I7 zM^9)yETZ}}x9+jtkJ@jfVxRVC{?ScPDss+MC5fHS4=qEZL;SyKX_D5A(Kd;CV4wmv;Q6|w zv;~FP>6vB;OK0omrcu~;;xy&WsfzQ@YP5{D3Y=aR8F7+cRNb7i#lHxhhtD)US>_R2QW^M|({n@t70!z@{F82?2qYyd18k4n97GSKR) z5D?Uq7#_Li528_yVa?;VeUFs8WwZ#0c!mXG06W4yg6%Fj%48Q0InNrym%7`ti+uBp z<9{Oy2m1w{HPQN}zK@>y$)0t|_za(&)1R5iM5t333G8<)G<^1H*`!_q7YPVq(!v!n zGNHCuxMg=f(Ei3IS%42nEnSxcOK26~a*I)S#+dL~HZAmI*|C#p)O3S_@M*@v5S zrCGf{^JZW^U+D%Q4=yujQ7B=9t>{$q<(Ah|5XmB&cvVtlg;$WZ`np0>ga9v~60xCS z3Z`8#;5*|19vem^0RG%ch#WCx{LI3k?N5itupO3uDrxzhM|-JWn`Q1&Z*~lb_!;{B zlt;7*gbKrGc=!?w{&Y~ZrL8q%rble?0E1Kri*{$Jx)4eX^6yIb#2sx9{F=-~-AI5Y z$8i!0U)lw)&qDH-IDqIIA;U&9j~XDE#4ggIXNk@p+2_R@LPAfyzQg8k037o;miCZ~ z9Z}iHl>*=cQOnytcuLiiNGHu=u(y8JlSB~t-_8gq?nH7q*I310BAkI%ksE+*;SqwF ztW}Gwx&vqr=cH&!#@QxAWl8KuW{Miwnvc2wuRUl=%^`HFg<)whlh%E2J^$6$>VrIe ztuA=gr|2QGUs{f~WPdeC!7=b~ct<#=!p>WIMGFgw^K4J#o02F-={Dlm;Dha%-<={N zfFB$Bc7%$8Jsq&|Q9_W|*lPjuDfew_{B;6F?N}+bK0H%+YJXa=cGugec9>{=+jGOI zc5+s)_UM87ZMQvEd}@W38exyyqYFUa)FW}zN^?;sZ1$ls=pkJ4rO2K5dGrN%qC&vz zGgepyzzF+NV0&UdeH__~{2;J}2;Thh**lV`bd~_olc(ncOuQN4aI1NkVLLLkstfrI z{M-i4_9HTMecjgh?2Q+@KW+jM29qQwmU&pff1>o`TuLiP$`5BIb9Nw)o#O%0+Jc88 zkF=31s`IewU8+bpf6W9AVs{Y`05iJXk$4^TG)C%46kn>0DoQKak~yody>IS4<+;sv zz=P${r;Cm6Xl1f&&kq&o>3UIGyjz5Ml0)inH!Oy;(^xQy7~O9rYriRwH-rkgg<2)3 z{nd`_ZYj|5VEoWnIvyxEszEW*Mn;821kQmv+9qtOWca6;vFX|5ccKU zzbowTkfLvp{t7KE0+{&x16tTou44ZU108jHwY(`M+lP_T`rlLf*EL|RAp4eG#1`;6 z;lDcZf6;G1*A=>gE?@k;vvW}?xW0eU^tZapBCT+?b#fOhK1wevzB$Smwd!O3gT^mV zBNbj)Sp4|M-@NMh>%I1y0EPFJo_}8p!TrJGIjPxrDC^nCi_q9)&RinH-@-xF-8e}o zeV4a>sQC5*wmV56&MX;S5hn_vchHc|wGFu132^)1*d^lP;@%8qoU^GXm?k=}{hFEm z5maDeYJn;U@gxx81)+FjNzPP*0<(Oi)Fqd@|-)x;6c{Wi8N`Vc9xPB>b%oreU?%{kGi}NIRWRi)n+qkU4d`e?o*XD zG?XtFsi}551wp+^ZMvnAcTdV7anJ$#&~)Ju{>#f0eOhV9Uu8i!Bu_$T_fUbf&~$C$ z3TED==FKxd_a0iULW0f89U#QL$94dV5SmPzGHT73Ycr_o6U%?9|$+0$Xpx=B&X2KMYUY;O1pOb zx?CL(B{jfv~EE)7e%zge@QOr%F$%a zo-%T_m{CKhXT%b{|FVP3!$K)5e3*88*|;z$ePySVS#$H{P=W9 zPFY#mC!uDTVbz!fl6^}<*$nGQ!26=0&PA`oT|8pUn*FSJA+v35fAudqI-;)asojk| z=>jdM<56DHCz3qr?>X{WO47?T&cp86&F0zsC==+) zt(2y9SXgaPR}GQJucX~_SyutvEv2`?Ek(e|4OB6=~CN$UOZzYA}TTrA}|>Yi(+aYMkyIi*WXIQNXM7 z7^q`$(c_V`Db-_N-c)ZJdM9Q++`{bUUjs?v%+RlElvL1r0z9-e(JUH ziakWfuFI5LW<-de>wLJvPKYlJqfo5H;XU^qJ_(ogygi09lf#Sq6;qw=p5xZ{3-uc& zT@HYmE?6I(q(w_njME z#bT-x_S6dgLYjVmT^_&Lh)d0iWE9uk>)w$43Yt&kw&&ezxlQCK?C3D>7hl!S~N9>?MF`g>e* z7ac*&@W(6D79tDg1)(n)3hhml%tU<4t-Z;}r`Awr1ycUiPE%KOZDZUr-6@@Mu&)oP zTUuLnw5Z-LbTj3_kZdntpS*2;g8Q`3E5j1g z`^_J;F^!+ykuVA4dEcAN2vP$DNNUw%(iFVfxWwCT01F>~T8~9o>6&sC&N%k~#KmiQ zI!nfTjr*|uleg-u?;n?I;ilnWQ=^>%f6QXNaPRql2X0Hnq_lL%WI zRYWo@EbE!l=^Y-BRpzuXL)dBxO{cILbb!e*`v3;{eD|0EB>U9 zK9fib?~1AMEYv{>uJff(%!gS;5P*|PHspOKu2rr}pj9Irx+Bc=@9JcJqp2yo37OlG z;^Ivn5?o>wdNmYol7=5&u5xP1j-JZSvl_d;BHb!BlVl)MuP#kdgRLbU*sMq|)H;=A zsHGO3&^a)%LQYJ7j{sX)aGwt_kkNycf@_annPk=*7r>jG=l9WEln>AnjRJm#ZxQ0P6`F#r2B+vYLO@H!B@)qo^Jrm=^e$RkxT_Z7?uH??>bZngY}7D8_Id1q$=Sm8;7514(P9Tved}c z?17;InM6ICY`&SPqUSwVjvCNw$Y-2b&9U$?((pGqdDh3q3-GxClQZwaWN=S~Dvbgv zUZ3QWgw)+;ioeU9jePI0zdIi7ZYV)fve}VtC6YRhB;k$9i)(`iw>Q^oO7O}Dlt%~o zO&%LPBV8)sCM`zVV?M0d;OOJNp{2&bfCwzid1RZpBQK)78y_3z96-Hoi~8(v6sN!=`}C#jD|67VX&-} z2;F*kO8JY(I~=@BmOz`>-K{LxH6REZD7V(F>&@@}q`i>1qU~`e7TYmFCK3wNAFem+ zxlF$IrJy?K%21B;10mY0iX}#f{wwsATOUq8?(^%67Qix*zUj}khB)<+K{WDCE61Da z36%Iz2qIti%HDKlj)Sk7J5Xq z%RKAV|NEFDDXy8MLKA0nZ|7yO|aLv#b zHJNmLk$H<|Fl!Ya1dUh4!|*O;as=Mr7x%v~ONoIZ{(lc!{gS<#Nbdr|BO*ehh8$Ux zpEM>NQ5A{4rUm7;`+u!&srewK7}~Q$rN(*f?bg>V)``Y05LM|h6L19J6)U#GSzfmB zRi6&JdJ0>Q5IU9QM#=Z;O64M3ini!A>B+jAuOcFS5&C}aDn4L^)+sw7^y<6KqY2l8 z<)SMh#&y6}$^*-1nZ2%co-<94j`Y@A_mCA<;;=%7V8x|^ngW_nm)G9Ohh)q)_|B1z z<}ZCXf5U|Nog5YF_3loEu;C^{tQ1LnbwWm=Fd4eYNxUjKiPW1Y=YM&t;u~OhE3d6; zqK)%7He0gqKSe(ybmU|`OXIPCs?$PhcSK|er4**UabbxyyVqo1vzH%T1v?F$RANA{ z4>^TehB6Zu4-d!n>*U{_L_Zg~(_Yqnjf3L^D2trEU<1L|o~ia7+*L7A(a1m39lscP zPjw;1QZ?E{R8|G4qxjLE@5#jUUobOa!zAl|uxs_ronsoQR($QE6<0cBZ3+8h3 z>O9qC*wBUz*Y28RhWfY8$2-`HCkUpJzAmpUxd4?E>v2AfLbhKXtz|yiwfX5>w$1+i zWktMp{9vtopQf;9_*%n5FDB`7*vGBD+(igOT-@RSI&dX@JX~SzFv9s2Ghwo>V=+IU^cdv=& z5ckp=_&wq^$o+;OM$3AE0MX#X3U7s$P6RC}g|bP{!C+ZGwT_V}DEH2p4Ws8!;6DYj zvonV_9E%S0Fo~#Y-^k!F)@KyFs$AMyhvH5*E{SH`9ufE0^)DNZ5%7{aUf>Im+Fx11 zUn=&mutx8=06Ag@wEN`mqIbfW#F*4GdRp_u!((MEWlZ~mWSgemJ?^@vLO^~=AqJ>` zz}jwoLcH!(g^ga_tCaiXsN!?Xcb7&8J80{Xo7*b*v#ayShk^7TLqf_5&QEv8iM~X7 zOXp~0`<)+PL>t91*x#Tx&sJ65(KijKK6$dnUki3hhWOKk;(iOzBSzjeC@Tv#KaH*n zadzZb+#(?Bf)#7;uFXPGa{$G}OsH2j!o>ByeCu*v$oA=xqP+#-{bp~zcQlN}Hc6wx zMxB<|L+UEn7VX)?ekV^FTjZc}5FmC`-~9P?ny~l4?e2s+vVI!G%;F`>BIl#aRuedZ z%R<_rOOJ#fKkaMikIV#jG!3|;?JT~z+z`+$w`$n-;@2S+B1!HEGHXv;ch|-PKuLPM zT4zq)^Rkrge^!DG_SQ!Vl%R>60(YazbmeHTi#m{~>?{c=9DpxmK?#qpaw9d3-gRm<1R406? zeV@3r^ae1(#sUT=92?HZIENP)z_Bwfu2GOcbBALeg=9i!!ae7+l{1uB9d;EH({v1a zj@zK+bc(kZR%iBoXS9OBK)Url>F^GDKJZP6_WYG?NLOPk5u%@v2ygtnVz9ad*7Q+)=32KOx z0`irs>22={+k75G@-(bZHiUCF%y-nMo)pkDj%KjEsT1WBz)n(8s_EVZ8Yqs2wkuT; z_?0r*zG);J5KgohmQS@sM^|^>0A$~vX})FsrLE=;>a>bTQbOsaIR#YHREvrG?olvj zP0_tyV*U%$zIS3pW*z~nhmox#asx9@$?vg)T5OIt(Nu2 z4S|D;#RTW9R3hvI2rnb!;<8)H%hqEc=Huo0`io#7h^gr8q_Rw(s-*67*NpHRH5)P$ zPAI)Ap!*1E)fvU_O5AF*13_|%>(=Z+tB>%*hV1UZ_Q&QAc6(+E&j+-Id_(zo#4(5r z`znj22t7cF>Yb1e^{0OU@+csiVldN`Co;=QIHKCY6E$p{M_Hz zrr?Pz0$5FAwc!I0WS!8z0FnO%F+QM*<^Oi*|ABzLTX<6Iz%|wI=*)B3_Th7|693O{ zL5k7-ulNVvwf;4|5N|tTjw)ak(%eAIx zWkGuCrrsH;?Xzl?x8C+i-v=8>_v*r%;!Li~+*#XHHg%WY44`5|t?6Ki(@ zW;dQ;&^Qs3At)rcfVAUWC^jfO_wLN452=PfX2`Mr?`as+>WOMc?uUnHD#M9NLH9cR z%Ml|=xpMQL*Eq%{GAmzKgcS8wLu$N!>D3hJg>%5tX;Sm_m#bl*AqMJ)N4_&6q%WXmklF0j_=B03I#iS`An1CjNb;P*2iav=*)L@tYr z$(IXiED7lCf|j@=6BT1qeGMRtF$?&Mm-E}QoyhK1Ui&q7A_5YS z%q9Kk50=jOf}qMBy^8g#yL-}2FfjOu@t6U2#8@zAdtq$|TLI!kUE7|YCeCeJenTG9 zzTTrnoPq|4Ojska)$$6DwQ;)DUm@;HOquw@9vBIif7#I?j#WPH<65d5@6U4Ui#b~C z-%xd%O08x#_87WYaRlf|*v-u_}bJG#-nmM~8d5L9d=21IZmhB}?Q-r0>p2 zK6M_f-7T9+@_Tk1*dn)N5Pc^|QP+~H3r#>D9H!Y=Q@ir8yGIHja-9V;UbPM=)0pbURGl5#YcCtw#k|=ghF-!qhAw`v;vH9#&Q6YqpjR^u zi8EN7J%z15DMmoo@KSD@!hNbrzPv^!B~x@7#&`zqvpP8C0y&qoH_Y|FOEN-U0wW<# zjrz<@3F4BQL+WW|>Ai_HgGf@eRz?<(U!QbGb2W7CRQ4X|Q0y;V&hv3H%?q3SJ}xtA ziGK3kvSK7&=16N%6+RmXXClnL)Lp$Adwsv}UoV<=91*7vCvuBdueY@SC|V@L>? zYj1ZI9JDd%BbSaaQofM3=7yjsIdT?;dT|xNL-0Ou2Ve|GH#;XJdkCWxu|S18BWK_Fx{} z{P;xeKF1~ofqUJ@sO?QK*%RUYRYo!8h0@Bj)B*|c;?AZ?Gn<9oagWyk$!KKwD#1s> zbyLFgU|Dr#W06-mp>kN0EFE;J_Tmh?0LhKoG>)F0=R9GC7s43s| zoB~ArZF9dcC}|Y7u~tD*Q`zO6)4h`lGW=1P*u4opVuI+!jqH)7i**wo-H*Z-bk!ltEoF?J+$SDu&)A_m( zH6(BaFT;{nW6J!Prmo*)^3$YU?KS+v-E<`)$U}PR?wNS4ewf^KF7%T0)2k^#B~6h%OWl4XLldy9}kR?tk7(*h42X{KPh%5BG4878qc7pff-D`T9xw^OOS)_df_% zMpF;SHC8?_s{g&NimG`o=)`>6D95#G<)h@Tt3uneL6O5ixk1%JTh% zh>CwoBoywQAnBq4LFMaeLH?j%WsY3r?gGa((qJ#F`oEijx&yKok1EdSbghF_k5xXx zrak-d5ayb2f5rN+mM<-IXOir~7JU0@gFSUQi_A?_c$q3_K(;Yak=i61qB2GoPo%3M zmUt(;7EI4v+?PEnC_(Sj%~Rw!23~p{PTQKPn!E>p6P+wSPwBUF?RzM556y(fMja_h z_;7b^8+jRIP(ImXI{6zF2Me{r*BN1+bWq~vJ0av^lE;aa4)5-E@A=lmXoEHfxpL8! z4%KB7WA4#DldCjbM9R&&46~?V3jyO8PFFFN+q6ewT4>c0Zq* zUo>x*{luQK#wuAeNvxshsP!hDxyq)lw$(k&>X3cg7&5~(yN|9N7+ z@^D?oUHhKrzJJpA4_AF-?UA_uJhOlFJ=UZ)>@iKvj>n^5g)*NHWUf_Rr@#Ky*0lZe z^#fD6dwhKl+CuB8$xhLcD9?H7tzm9SyFU(cFkqETuNS6-?T%$LsO83hnDdxvZj~bu z8=O!C@2mbbeG|c;!{!?MO^jTbTO$6Qr#=QCZ2&~E#dKZdmQK`bv5HCO_@v}HRrmoz zYcNw#fOo4K}+kE@7)nPHXJT@MX#bw8}5qFNJ)N-ONI_U%Q$eN5tdDP-pQ-DI&tFwBG`KG;G;|kQ9!dbapd&R45|}iO z7XzeJ+kRBR?_EC6Dv|ENcBP%_D1goVG-wDEwZ3$9z*@ic=eV$;zb%iK*qO^aAvI#3 zO^lA~p47gpn;T>3c?-v3In)v{z8JAj-8N-{t}_mm6J^SN;WA+}*zgG-Ffm<(2r@*euR}HI~FM zl#+A}d`PdxdDzHdZ*?q9ms#?OI&lNEwo?gc6|`ilXI@}uFML>ApXAVRzm=!!X@J`2 z%edGp$uE_(2)|oPPm`x#_Uq+0+UY>e75&-glE4Yb+k4L#GNpVsOuwAiZ;Uv^uDfL=HKjgW1ev=iEKC!)UEIe_J8HL*4$q<7C8tES%ViVo-_l z89u#1xwtiS@Z=B(0y~*uH*cW7%&ge8k%v zFSa0i3-0iKdPk>7Akt|8Zxcp+9mVtaT8oH^;*E7cje&!`*d$Rm?$S0?G1I*js^M@p zJ*Y5v4#8BtQH>Qk%X050?5;`YMJdw$Ts5=(*6a^_2>JE}U+}q&99qpmVp!b;CVp7* zd^{_R^?V4kSx-9DKHWC2t~5_8hYRh%s&zFSVV19uMDf&KGxAM#Mx{I+5?(JLd?^SKiF*xv|p>ZO@9 zblXh=?EVrRwGe@Omxa0yq(LAul{s1UalgxB#P3wi4htFXDd)f^2&&#?<5^uusehN& zfubrI(;ylYJAk~sk-00v3-qTy?A-kve|n%!pMxRss`LerI*TprIBpZ{jX?M;?*0<7 z6LE_+@-y?pfw`tEUNE%E-%*`7OxG~5`DA3+-NS*kqRs!aB||4%2()8?Pp(qJhp245 zmw35cW_G?rGY14QLdpwx{4bpQTip0;`ObiS;Wwusbn5pmNxmRka1P`V7%fqbmKFPN zzppPoU)|PeIC!<`2J#FWzY#3_H&qJ0*!&+2=O2>#xsx+r)=jL&w~An(%6tIVMG;Zo zyVgGOw!7c-B&TOLV0Nn-3(>#IOhhvB+Gf@Vvy;@B{uX?a5<20l#0Q}u69fv4To)4;S0gRy=5)alN` zL=D;_^cPyU=I$36-d9!z3EUdhOm`KNzr`Pqzlap-_f7A8hlcXn`1$XLbF7=#lv#i|4* zdFpVln$!PtEo7Mo{QG~#*f@h|kMR`6C>f6sA7p6nyo-MCcbz2rpFvt23Aiy!dJf1h zXTl!Tp=2Hg6rc?Ub_gG}i_T;3S~*3-u_xi6F&ZKo1*bm~7k3{!Qg4F;#Xc|WM5k=y z|IA9&{d{X|98J+1jON++@(b40z(=!qZ@-Fh&%lijs+yBuUof-nPz08(IxWoY{*^M?6&^9?{;0g_mTSgztr>e zZFZj;#o^d_{+s&~zkr_l_>kmDWj;W;g1xah8w;_X~?Uotl za#Zd%WlY}7#cj(@fSvY_IOt>G+l_Oh;Us36wIj|co9$EOt!uq^%b_p-ylswoQ8-fn zd^j{a-u67PQ|7NIK3?b1Ai$~|*mG)k*uEkPOuK+#GEkiX(|>=)Q)cc;F>I+N>O80% zI|_=bgK8w%jB^g}I1}|L2kD9fknA2Z&)MGTsC$^mlZV(tAjz1;C<`1oeiL?R(m#A% zqZUu5$y1f8sKZV{7SJ-)6W=FSWawo#_!O3^Q}zrb_w&1O7%aVgu=}~I4`XQ=)Jzc* z-Kp=ew>%EUuL$o`E3*MC&Nj(uC|XvH&SLK4L*8Ddn1w^`&5M^V;m%dlUQr|YQ@HivF}0VpwQ3eLg%i2#f!}BUWOH^)Z-WOE^`hzsiDoL7I)@n zDgWmfN-l2lbId2|JPtdhk~Ckw>h*$I*ZxKD1cEBz^!`@K%W6m3ByaaTp=}O~^LTqT z728q(tK`6mk+H2tDu=DzaZomMILF^He(SZP^{V2lPY#PJGfVw-FRu*iBcA=%o+GqQ zE0Ef{b%S~G0foP&@F$lOc;?cL${6alFm{C^!ygwI7#OZi9E)QQpm36ugwu*Y_;fG( zqoB)=(DhzNCeGu`T)ey>t$4L^tChJwRgS97x+iPUfBE*X+lf~5FST{h*}?wfx8aTzh-0NajGoLe7S-%_IrHMxcrFM;o6hi))E2+ ziP0d*xrsn}=6bNtYkvJL4DO0S_<-Y7*dgH030OLBJX%BP8Z(cx-~1!T!Iv%yy}FW} zolOGMBR}8s^pBQtKXYCPa4Uf*X@?Vv3OMC;aj)&UYvV^!1>c@4ySK7Z4hhe}Ihi`wC-vh^lo zi0YG0=hRTsk*-FMeHd2EpY;V5y7=wz+>#dbmX+?CP>#OAxkWm3IH9wAZq*-T2<{fI zr@PBRbV_N@=ZLu)B{EgbT&tr9zD`*cUBqcIJs-%0wK(_J$#Z-Eb{@Jlsu-1f$P*C? zYsnBS3JtgZ6P$`KSU%13uXd+1)@FBKFqC&1!5V)0Kb?aJLSEGCerYgFn0cp4CGQ zP?~wbb$S1Yp5>V;&8wuC&wZj|k)U-fjDQ3w)wko;()bXc(o(czoYSj%I?<7~1*c@M z^3!X-2-WXiR(R}9jvc(LN_-8CYJ5+#E6w}Il6EYLJ}d8K)>*A%lpf1a_0@oP!@6?)!mMxPnYUNoTyi>{+8`L#ZGx6 zPbv%zeobP`TKs{MY}_f`r^ZV;fKNe%qBw+}Cw*~0lk7>Kzes*zwFT@N?=CoPZ7sX6 zB8%4ZD@;Kuk3hmrsMrr^2D9tYcrYNA$4H`~tF5zd(EA{YIZnyAGc)b**)4VVZlseT z(xAw_YD_G|q!S(sPM=p)R9!WOcM`|V(SHo{KWSI~ZCO%8c`>!YL5g>#pk}=ZT#6$T z^$g|4xiDxbGe|oB2x3HuQ3hW>AtG*oUm3<*0rnJ)xx_C1?7<pX3^P7YACY5Pk+Ta(J%PT=PL5wlW*85UD_^L&{3$K*2%pi^;_Bc_vq{9Hi~}gr58vU?YX#V_cr=EK&^&S3zNKfFNl_i#|tDUMqF768H!ia+23m@e79rl=+l z!$9Ho+@F;{hX@pV(Sj9O=xa<~=YJRSf8lBW8vw8@eEO*lK&w0FY_`3o#!yV$7%gL@ zI{)n&KewJjj7!T#PkAC&;r)MGmE`$wPzQp1ly^1V*O-FNg41CreRYfSqQBZVx-a|{r%3rd=s*`8#XxP&AZf*m#rT* zUry|=R%f>g9FKlF+Z>WhR0{fkD0>g6rnc>S7%Mi!u7H4wf{GXc0jU9LDoq7JK#0omG=A6CPS$prf)|_h{ z#5*{7!|fx5xY3knD|<*WRxr0fPnIW^v}mZs-v5J+CXrTL`_=!YhR@a$YzmE;rOKrA z+Y_YIRMJlzI&)&{v3RZL~*?3_2JRSf@^BC0I;? zWd1q6!hHW5Oi8J=Y&t~T7f0^jRZ<{-m^z;Px2`H(th+tYCGS>7<;}wzJKVmu(Xh>} z(43(Y7bwTClQ^Uj8#qLl7vW_ueuNu8E$2R(*MBnqpsV88RSHjO-1`*)4dP%L#f9Dj zX=qlvzFFBlW_kvwH-aHBmPd~sk?R8k10VQs{tHOqs6Yz;Ux5_ZXsSUGR{=l&=%l0l zfaM;`F{y4{JHan2GQ;`N+UT6B`mW494Grr3f}d_mXzl6b6W7}FylgL&KTPqixDKgO}chbih;aYw$!?cY2SFg#ka!{d2E;{V9luX3Gktj%j4gN zM7hHS2z#1ix{eUmiRT}lDVUI}Sqjh8997Hyj2|!lHsjbHXqGpQj_B@k|1S8`bu?Vt zS%;6ry^ioz)twGDa_uQ)HL2>}tsu7edY5E9o5>2Kr_hcTLuzo_IRwpH@*Un+U$xZvQw))(BkJieQi z40>u;I*%>_o?VLo0}N6n>|4{yyyJ@WNE76hZoO-uevzhMwDw%LxQyr0)Kb`t&ki^P zd$h;Q31ZJPp@X}QlfJz{kD`>|kC97C8Y6T-w>8ZfeaEUBv9K-Esz*EHmq$;2nG9v2 z%9+@jKnWA6A9Ffd@7dGR3VQw=R)4=a&li(E7P}i2V;#46DQ}GK_=t570*;GgvWg?jE zYG(&}l2Q|a!`Kv0ZqDGy4U`E}8r&g3(fx0r7rRIx@HABbg|{#s@+e`ij;?NYCSN|1 zT_JKhYPWT>K}+N(P$W<0DH-pz+WdST#W7Cp6!ThS2r*o81G-cxkQsY2Kj1>xNIj2^$%^hoJVNv1Y$>w7bcJ=yU_iX#c5hENkn^cIu$w!%4hsm}{&~ zh`SONqP0Qk>OwET4AvZU+jHmFm-aOxEGk@WZ3}ZU$n^-$4p--| zBOc~8G_OY~1xN#+OI15iwx|NIB_G&_?6>X?uHCMTHU<@mto7!8%L9x{wiVNOv-9;t zFPopL!wD6#E&#joJy-;rJ46UMknvW#suk(yyhnGnm=e`;kOHJ&1cSzH71%YP0>k`= z3M?eJt0q>|rEuC|zNa15U0|pL>JR4bDJrfz5G*>=0&n8L)KpFn_Cd1A9b3bt1q1h% zLmFm^%;q#9(5_LDi$Qxc0KmE+dW;73bB>DG;Y{=)m0Ynygbh|yp!Y7qy4K9_;&eN& zvo?+QjVWocO4)n;iQBhxrZv#rUR*^V=?Z z`I+@~iuTUK<7|Pgalod|zR$twtnmxLn{&5ps)>hxWMbnYBD}k^HRq^lr$*bI6tu@`hZ*U;=?Fu^ z87^K3)K};DVQe=u7PSClU*@$FvXt^Xl(qvhv3Az#`uR$x_onYOQ1G~j&FMs^vP?vh z6VB<2<+s}hX(m#TZ5~53PIRVCRZd69Nmgqi9vqt`ry0WRzDGwaU2>mtg+~+KIHlnER_EYmB;I?XUs{7GP1M(Qi_rB3|jLv1J4p6G^11u9QaJ3{yN(CLo3VV5P*q|{VR6R*0C z=ANetQv*F4mSk+A-v=(|FXt*MgD5GpO@^!@{|KScS$Fc~9z#)G` zK8B!J_NsEqkrO9R27dV9@i_Ux3s3}{qrd<7@#9z3^YuKMK~-V~G52ZSZihj;v+PQ- z@7ZMiul5Ri2n;z&hCRu7#sIpfNj-Sr6G|>N>rVjZY^dm8GF=&8E1bkYX`r&jQD%=a zyKK!s=x)iLN%sz*Na|b?KGc;-&?ac>4K?v^TXmw3{ZC{4rWpD!S*HI399_bJ@iq7L z0H_!+2IBsboK{bvXd#x)m3V*J@S|aLcIf~o&1qm|pNDMd*0-h&D>(G?2v@w6+}?e2 z%>Mm2y%a~kzYUCx1Q;`W3RG`fQH$M7-lix-&FU94Q)>|?5Sk_CpoqYeTJA;#9sPtR z^YR7$)!DAY4gjpPJXppyLFPz(+@{?(2ARIm+1;@dNgJ17roY8}(<4##Rcnk}|3@fG zK}Bjx>|;b!?X1JNON;Yy7fAoM$9*u3if%*{B@cr-TYXOuSy20KfQC`4)`MksYoPFK z-?JwEmp$>CQ+|1C1`!TGmBB%|@*r6J<;yI)BANAG&}7I>S=_%jOP%-j%bvC}hOx1+ z)n%F)vk_0B^RLgh21KVwIq$#Bt|8aJ`s#-ZnFt#JwESZTH39{qGLX8<>)t6RabntL zWLSk1vsV@Mfq;h2u?hm(Y}j@|-TFAJRP}ha>(4YI7wMiG0GztzS+hJ^y{DU2QfB{N z!DloK44_^O=m{EF1Js8~ER?E2`2!75NVlk-Ir8^xf8B#e3}(F-$=DJYm`UOYOYsx~vLsQ)~Y z=7tf7*u&2k`ijU4mGMVY{b}t)W@1K62L;le9De7$OEH?}DC>TWlKmM^nyz!e&)Z|Q zL7}pI)EPrfgWBJR_2-U}a-zSxOtVU=ie0I@bi4jcS=3Z%myKuyQ~a+p;tOfJl4`pU z$>^1)d8xG0COTeX4_pcKYk04B5AH^*r?DF}f}pzHSKB*3f_H^*5)3xO^6UI2O(*jk z8(6(!DttGa=xFAi!r6eJY@0^AHw5;tKaBLx4hYsrcc{DRzo-N2%akTL7oV=CmMY^L z%H-<5ewH}>pcpQRd`-IA%_qNnC?WpcC5(-zl*GXkQVAChypt6_e(cfSz0#BM1BnUo z6Bj;DMB8}2)o-})5qIhH*N>smyb0%)5jb?Lc}Av7nM;a8XiUr!&M7WC2azGU@VFIy zdjIPS98P?c6(;tc-8fdAZ$!ysdnI=MD89zi+gwS-Q&#GwKl1&XLrwZ&QL4k`7$C~tfo-36do!M(r#r&;v*EJ+Tm!dGaztW`9Fv}gTPx~afZd@Y@D{;w9 zG+SU0eI-i2oZDz_nHB?W1|Ju147{mDs~r2_+v_)ON4UFCKSHyGL%4)JQxpgFGcxn7 zCtqY}I_w{GfC+p9eQ8!F1?%-5gq}D4Vo~mlb<3Etx^&${^z`-HfAQ$g$_rW(#m)L} zp2d^4%#t1_%RCOfp(X9!5~*xNCW5|sG#)?V9VynG1`ixaU)CmCRZPA%UCNGFSY_@& zy%}UlX#{PSc*9=as!@s7VVuo-(6HC0c0z9U62U@K;8u}D>taCrkMO6;TI_s@Ly!Y7sc4zHtRS-Ik;VtK1Bd>#WTv`1C0dun}OUsJY!6f3eP-EOYdpWAN< z0h3mO4k9hx1lFQsmG#@=6@o=w=!e(bR~ZOS8%Gs#g&BGYbh_f*v)76 z^=Tv5xD>Sv{NhXHN)EpHfxva_;f!d+Cr;MbtGGSYQm#7rY%0imK>`2mIr^qOw_mWy z+;=y*M*+RMQo;p+7I#vvRNnd8(>)`scdH9^kIsy)V!Vr3F0nS5Kit`Iiz0Cd!@6xa zhq+=;pVLf=(!NI%7YEUs*(H{vySVS>bgNx^`1}~C4N-(H_S$5JtB;)0-JGnhrl*LL z2o^lghSOORhad&6_!lOfMn^@l_aM&8-(oxSCl>-=btKEStHHQ|GPQ4yp0sMsK15-j$WnFvrk#QST|bMt+*H|qu(iZP~5 zlq!`@Z2G6{R}LZ(l%qWh6o0TM0sAzbYN}Z4K4Q}Sl7-JqNJ&1sB%_{f+>c2%>PX3H zktbq8TkEIrr8XPwl^gbU@44+}0$E4#le-?oKb0=m~cb)(K*Mw(Em z=ab%38{OE+(EGIF{jyr!b)>?)VzqL4DP55+i#npjMz#Ljc&F;IG>TI=F@1#0q5KOD*yu-v5>WV&PNSa19Lfh6PtQ7fR)wAyBa;ojR zh|fQpo-IFTliC+a8El|b&9|lpGWIb#*8j&wdd(~3TBs8y!u_`1k28!Sjs=jx;fG3| zE!gSy*16%1)TMh(p;xk3#?!IUtsg!m>b|UbXQpGTt*HqaF3}RD%bap0u838U>$YO? z(Onq|Cm{PaBP((ckFV&)C5Q?SheeT&($E+)#nv^?1x$+Y(~B(J(XD@fsRb3R8%oNm z&$9Gsm{MJOA=SK_TPP!2w)nbk$P~MM>Ztv*Vjg2WgVF-V*7GO>v#!0Fj+#<+h62kH z{mN4A*wn+qz~ZCXf|b8u!YdW~M;aImXr z3T8b*-HpnX&9X^^5mQ|LZoZUI^snWV`OkSoHAgqVV7cccV0mMoJy3nqC0#$D5c-G6 zPdWk*EY3E?!7sRx>^eMMzFX5N@+GN zTl>hzELw@A%9t3Ljxgt9hUqdOVWgB$Pdxo&fE1F>tEM~!jepsFV^)jCm7rSp_)H_w z12|8I3UzI8ILJ>u9K}r{q4(s23?0MVol4+v?$dbC`$1jQFo+$O=D0F?+pN$0#j4X@ zm~WBMamm~Y9*Ji6wiFVm>FZ1lzRG>~rO*$EhyiklZC(26W4-VhA~;kpm@Mi!&w9ZR zNg`3NoJ=<2o$kB5dm2H@FA`&LwCKHYPi;kLUZc&)a@4Z<3Z8K4462M_2qrP~Y6vDT z?Jgs*meuzcdhYBJ4AeJibIN>ns===dBF>`lUM&dGJyLJbnF#KL<1MgaNmn+hxBHUW~kXV}dcJ7#!Kk*}8gl}l1H=22rnha;VW3FMNb*|?}lw_ugg*^+{*gujx(-eWDT`6+U));he|2Dk$Vfp&) z#>k>5$Eri8>t=a(IU4MOXJ1@eMy_S3M1YF21@1vMnhQLRRTlTR?44nx@KmRMw-{C? zxcjcJ+?>QN<%7lVpTtJ>#CXDTK9`QCj~#($?w%5?Rhcj>%uFR~PdHa7Mk7>j8g9te zMv_&7%$Il*yW2;MXMEM>j}J@3;pllCDNW7Ym#XI`@h%g1!bDfNGlda#Hg}_|6y`b@ z!wBlo*iTNPwHe{AsXNAXr-Bd`+KH^oO;|HSC&811Hl*H=o811#)t z#?Gg*%hrYZxxM?5JtG!D)1rnSukHVMw$RRziqd!XsZf*Um=FC2{s!BZ_JmVzU+r4v zjhx{B8?Ad1w$0?dxJPxwV0M-3k!Ssx3nf ztyaAFquiV@aNsUFtMt$Y2Ay4ZPU3;Meb7iUj8n~>bwQE8nX zKEcU2x8THnHWeGkJ<5db{cFX9JQ%-a-D_cD98Nj)aX{v0s9+p`WVefVb&E|*_w8;A zmd?DUv~;)A+1ju^d_UZ-9|fJ@J|`uxwwo`BQ59_?+2p;Dw)MFz#g*erE62(HDD0c< zF)*qk7gMz3&Ggl(EQcR=>aL%A(a}30a31wh`~1U(huav{Uk#V*znb}#n99>&ws$8K z_^^JpE%m@!B(DT?QPAc5>G#($=X8GqKu0{=(^+)&Z-wQG-1Dn*e-K~`k7k))|9mUZ zdSG~so$J{ZnM7W@MtNyM$QhZZq7HfP`e}su_7vC5cC!fc0vXg{Qd9WPF}_C_IsnWh z8l|PF8L~k;tcFokKjO5Fbe@yh|TRr=h?f?(W|!|H8tz^4h5- z<)`w4298sXPPES8FzjtBO*ho1iF$7;SxX2@HugpP9Dm93M78N@7Xo2wKMS^=TM28j z0wp2>OP4H4_EbJ$#&mtMh~#JKoJ%Um8KmHN&q5{j-qi$+3?7mLgB0 z(}go4e%Sb=|L|xRTM|dVhbGH-f1Yz8kPBXtyM*ItG#>}R@6lLenc}u zP8)(8W+g8B3^>5r9t$nM9+i8f9x(a9gHITNT1A0Yhrhq9Jveud4$~<==Z<3U$6s9?SG+9K6#WS`DAKq zdxXu|ebI_{EIU>FUuEqaDAa?5`oI3k#h96rH&{hv2ujc5?c0UZji#>H4jUHis}+JI zueX;=-T0s#!&(9^0!(f zLO;TH{hhoy#*k!!L$Z-y5_t=6F}a_Cnl2<_4!4`&Qp%My`T6^)?wl zl$&*_FdGQ8&@d1FLe>&>*}0tdIO+NEfU}$K4wA-OtB&7r zNcv=eu&?U8B;q*`mJxiW^j+D)K|W`g4TRttvV23=L1V?__G;}DmKsg4ifIl;=qw*> z>qEev+va+78fAJh=~WLNfOhEM>nq(3lsHj{pK2jF`XK9@oeEN~BEDN&5+VkG#e!ol>4mOe~qJx0y0e1bxqD z&*{!~8qf2-Sd-G=2MudFv5X2)#h((SVJ8aqVVZmIuFaLS>4Ls`sb{$Q9s})W?c>)z zr%m7CGQdInoTGAM$Z$-=q}hMpVgIdJt@SPfSA6&bR7#kXAWLr??^O5_L(Vj}_T6L5 zZt~DrVbCX3%RHiWSM+G03;$O0>wSbZ;tln(w(oCgSI2vL_Ec5mT;htNOi#O24bPk( zUNZSIGhocj!7FCd=WyFl)gqF&;A~4Yx$DZ(||rBD-{`b%u+~lKTpV>#UDY27ecPvxhUKT5_6NS7CTgd-aBO_WLycC zZ4A##yobYjgop1%IiZAR`&{hk$bWGc8MS{;77K0LY`-sOs>lA`$u~vb60xpbshF0# zoeCe>zYVZw9Q4V(UupA)U~kZ*Y9e@x0fKb3QtJksJT)_y>oS)&aSjh6aQQ;L1~Fy# z4#6e9_p7)*_gK34P+xC+1S3CD>LpED-TXW6`x8cfHWD-CEg=^FN;uRA~^>$ABUSY2E_e1Bl*Rc}^_|JNcm^ zSRECu;FTEhFoQ-zEXJ*MJ4xX1C`EoF92+xYM~dK4&z&prZRqhM>bAhKD+4x?)8$L< zN!{6J8DuCp3||g@su00PdGv_M6_G0zI{Z8^G>>;2H>GgfudTj0Dyg2s&qemUERtc? z`LadDO-SBnCM^m#TEU7@T@%Z^yurrMzKoYl?(f=Cv+iaI$o%L5#H!TFw6K3e-JBZJ77`SvG=*uDI`t zIZz~UEUY7!TS227sAWPzH%eJ)?Q}|v0|5ziJ#>tP_iHo?pI)$Ej^k$2ewg&*mPs#< zYK;mGdFsZUa3MPl{YIsxo`{thWwNA=A$@niR4a=VZ;Jc1j>EcBsLe+eotR@+7OG%= zXSkl}rCF>k2?g&QHwVt-VxaETdo^G4Q=%EB)kvbt7Oq`!L4TjO2Md}}*oZQ_C~uL} za39c_2LLN&@tk^>J)P!|;sA z#xtwDYA8DMaq^j#$Pm5#3nPIWQ?Uev`LQ@r0<6jz?Y8zYWwO6^e#DOWa?=JwRPHqG znq4=iRGf){U0POwrxRNQ&lFH%l5{5yR4+^vfuw1d9M*Gtq zjlK`m1WI{x+k}?m#V1jezuwdv^u5N2)2C#vT zw!6l*QU)uuR_}%-k8#p4DB>6?dpID|Y~*c7^heaKgh5Cia3H81=B)^1U7A3`ChILD zah=vj^tk?pg)|^pvVA`61iES%=&=i?e+Qf3^ay^die1@t_g!DtHBnaGlU;r`@kmLO^DD+xPkjN41_*ykr+w9he;@VSHPZAPJNdgVP+fhnTg;MLu zgtWA)qN<>`7o)KK{KEH-S*?>YRZaLD*{Zp~C1*@atZYjz3k0z2U>`=ahs{ucGAZ1(mCBClP;e~ezWTx`MW^qpU*Q7 zv~Zsugk1OT-EG(!{Rx5~J`R}t5%|V^bClM3wOtk*{bhD@KDhkNXM#4EQ%$>o68ow) zBN6mNaMprjwOSoSv04GHXts|x4ckIEr55@*Lf$S`=;7SSM7rT6gXATLM6O*{{Ojh8 z;yawPvF7h-AL=lVFz~do!ig6*3r_GCYv$j~v#;2D4M?m|^qap_{6p zhccspc{Gche;@^P^Fvu6u0-5{^u%g0J}QIELF~qtq}Ea}1WPm0UBpBA~zH zrSXOTZEkT67>Ig)QcpzOsMglxAemIF(qJ&8Aj*jwF10btc$|EQSQxsqiu7!h@0zdmLHd_x1Cl*_@c2b z43(LCB3oO*mL$|ZgObYDJozNcQTXVfpnAjxl?I&eb-AIN@uw62v7CTd8OzY$t)n4^ zt33;Q2Tq#nz0Lhy%7%x2tT|fHXlGI;f|Pu6*Fa7-o@Lx(_vR`}=^AK8q*ILm-E!pO z4&JiC$X|b)bU?r1<6H6uoT657d%46_{EUU%SIs?}H>b*30&N8~3g4x@>`h`O3$8Dg zizJTNpD(k?=UD=A$v%&=ZkS}K^N}6BgmmmqT1+|v@AY$&Jm7Qt8&Ni#8r%N!qa#3W zVgAsqbY7t7b~F6YcBbmrwi^HA!|xj|$ol=%VjMfgfcy{(FXl8uMr3)nvDxJSQth{Fw{*KgKrlD?y$9 zPdbu1X7KZW`0C%%Alv7i9vNLVa_GP1S$0MdFnsIVnQ)5ApL6-`nEWFjOtt5KOE3M~ z&f=FpvCcV_k2xtR{TTa)Rh zAc8&*f*H*+QUgifVpS?JxBK!5AJcOh(>Y7hYB_vo$OM-IV&se^|i@N6-IkSV1&(O2u2S`Db8B-zPsc!wLT_dF!KZe4aeh1(axK+3;p@K&o?A>z4idPGhp zL3#93t#Olgbs@gyqOG>l_Yy^lPc}Jm96MLUwI?_k~;0@+&nAgtTY1D=I0(JCV0} zzkyegq+0kqvMj24kT=F=tg*U_9YiVx$Q z@#Y{rZk2Ubu3wy6dm3V*oJMI|ND)#5bJxSRW~JP=L^-}V-#cQ8&*&Al96h8zW1Xun zUw0FeBL6wBQ$FuSvQb}7?bb?g%;EISH1vA53+^L}PvvQ8!CKD0g@Tbkp^sLQ?>t~6O+ zr9-vG9$ei1C!#DQGNKq73B(cV1@@_; z-iYb?6)*Uwm%U}eb>RlATnP?CQ5+rEwHT^UVKbsp^&G_D@Vt;?jF@qye89?M&n2PU zWLfLAGja#Db44Yi(boVEF5g33Ilxmu`K~Vt<2P|>jZK7{DM`)Oy&j=+-JV-O|L*Z} zl-@$9Lg6R-^YHZ>`g8UO-S@`)xgm}c)j$fceJ@8R@#@}|nx^yoIJ;B*eT*UXQ{=<>QM-i@c-;|wAZ>ugfDKBSg&5_`{*A! zb^h*QHI~$#)AiH6cO~i8^m>j_zI+pT{psUHNm(KH6-c1_WLLwb7;atJ`ks8g)S78x zRieP^T)AMXXO|6miFc3D8m1t{bs>64w&UGpRREPj+tg>r=jqpL z%s9#_W*??sDc(&~igS1fRYemCzUdpEMpcv~D`B3pWc15Y(kCSh{!%rG(Y5>>h_bHn z<=>zi<_U4mV$nNCQuFJZ-#opFAX)orVYGyGNBJW|X3_Sy**zCTSl$ z)NL)aHJuZFP*IzV`sytQpFWS+6s8GHl0y##r?$tpKb!5;&2}I7)X(TVRau(iOz_uE zT`J2;xr?OdkCA+fR7>i))%STkYXpBuGTk(H&HtB}VG3TQt~kl*pX>*XON`rqr68-G zCe&Av$g&xv?jwDHmfrTPWq0X@Cpv+9UP;n%FerfVA> z#P4l5qnkrXart01Px&-t%F?ipjoY(9zhK}%kKgARbgPjE? zK}tG^q`G#lYokQzm6oGLDO~~~c^OFKp<^Gz4lN^PY$Br?asDLxauQX-u>O9dR$yP+ zPuKh>3c82q#gjoKq`^nyECF(ysherA^Kl2UdUm!{!$HTU3mnNwI$(ZO!=`oMZD68-8keJ zU;k!nh~~N~B5{Eupo276fEu!yQsAcP5c(C2QH8-#?P52YQeeD<$abjhRwOd`+40Ng z5mE>1Zys7_t^^41qU4EC>VHSA&0dy(PSwbaVqcK<^#4o8rb0UwL4}-Io9{^QNJy zfA`k?$VR1qjh_O$l@!lE69l(SZG9^48W^hB)Zci~S21_YjEJW(>&c7juD?R_;Zl;^Mg*)g{{nH8{FcD5Xz^uull5$)(wY|*G>NWUx zZXraq8Nok`R5qZiqx4AoKwVW`e+*SkrEQM1uzp9I^~;$S*Pt@|gpDV+mG%`fcI*)MM{9u^AIzMarT#tJh%g!QC_-DzqC)14>r# zD+V;1Tad;2!glzM8piS4PE7E)nFtaxt;@SFJU#^mC}>``6y_xU)i}Gd!v{ZQmq)x9 z;n0G4#H%gYq->1evsSi1sPUSE!3n?!kC_LcR?u3~=?%>RxD~XH#IRxdbmQ-Nn?I~9 z1A&-remESzPdw@BLmbqEc%-jrBi;jrg-DXd56J$7M_4M$hfF@=jTLJ$HFiqOj^kfA z!J1=1bUyjV^>wxoxvb;l_XNJ5!@t-SNdbwZ85DjnJ_5e*^ZL%;<@*+R$wyLAk$7}B z0jHu)-nwDd318?IEqvL+zcRWN#)Q~KBcAbwz_FP61<@fxDFv66*WAcmcLJURI2|BP zp=!=!n-MRk_gvXF3a?eDIax5~ObawOU@|?QNa`i>X;eh7Eu(dps922 z4704VcfPawJK5aO<|v;VG3K@lMJTz<%yONoGz!_YbX(h^qoKKHXGy6co!U?vu&|=k zlFn@y4&?5L^S>SjOO{C`ms(U1a{~k8CChI=wIRbzJ{;7&@mVF+DWFy}0ZA=nf32h# zTE%vZmRHTGzPFqLZS(s$foE`{-FN-tcD1B48?b=@D|j7=al^tSZiiGk1HlY)Hkgq0 z$B|M#yZI<5T|J>11#8H~f8s)An zP83PZzJ+yUIF0cC;ePwKWjr1e7Zk$boZFW1R!(%QqX5;XhD$OZ4hf9rWk7DsIz2a5 zBh;5}bJm+49hUP<>GWrb?yD-(f*CLhb013yO6BI$%?cCk2UZ-pba$&)847BKjZ>N< z0FP;C+}yL`{9d6_W9U%HWT#Lwcw3NkskP{XA`|qdxb7_85V^zuHtr6Sz6=Bs;hbOv z825<`5^Xe0a|?CI0WhML0L6;nkJ|dOH_xfN;!37*WAxjx>b2S?^y7UmKNPLzolH%4 zh`Z{XjhRca{&p!2np_%$-rqnUwcy3#9?q^9c~{myCaGvn;LypO+TQ3)yDUr1){Iu5 z9x$FOX6_`HFM%L-DK!L|!yiKD@P|jq^HK&7>254kulLZb2#4F5A)x=%c!1{;l!{D^ zaXEV6PIl4RW_V*F;{;G9JO))=YvU+GNL$H5pK`-T$Ybx1$x@NHfx1att|r18q%;-Y zcpX9ORyxaii{!A1LOc_;))RGVecI(9A<2`RYBP4@_VWq!Q6?lxUNZh8v6>oa|&?bFZaa=+4%djK(92qwPT>3Hg0W63|G$InV9XnKy;i2_ zYAaWHhDc0_@K3L}oP62TJ4x_08zl3BWN&pa>*Ck#vYrdUUx{9&z~-E)*{M%VOWKs? ziaS!Wh1b>ri|o{Mp=sH9c8!UDZUsBZxhdazl;nu(UQqdTR~l_4Hnn`5tu~DiS;Qod z7IU5W>mKgquC;KqZPTRw=DuA4*rutz%BkXSHf8(=pgj7GQmL)c0+Xgs8Ejko^LPPX z4J~NeI4kV5mDRIHyoKIjM?QG$0TuMViiF4JR64*fEkGUr0`1EaOQ?>(7C^+MZGDxH zOfw&Jc4mR>y5qK9JUUibapUW?)~Ttpdt-zg3Mz|HD}8oM>DnZRG^2K%EngqdN0gya z`-^Rt%J6WJ#OCq&ABZi7i(%u1P-0j6NFW8YlKt_ULH@{Vd;rHCtC;i094&(MXA8;# z?CQ2dE&>;A2bClp0WmV`F=MXo+D9LL8f)szrCjJY?PtabIwcCkGFUEt*MA`5{Qa2P zoQV@ThweMQHjcHc1aS=}sfmK<>i1=1=dq+(M-*aW;5=^;&CW*h5S+X}C7P=77?^pY znef=!Z3<7vYtXZkzRYTTzgC4~pknkL3Btxw>X$EfGztvpxHN)SpTK8n7lq%xjC7ia z6R632l$a8WPBx{lj&x^nTk6a9n)_n!uI&p;x7N@56vt>|)6BHHH9}bF25nENOKuMl z>4%?+)6S)POk2@UsTQ>9NNP~7t)j2!hc5_O%v!O$9sX?W`^i7VxTLY5DNNT82|GcR z=M)$s6`|z9Iu-tbQL;DT+G6gP;W4r=fqN181vXBm+a{N;KK&y>`r$QR;QBx_Vt>FU zIvp3f#5$}h+KSrjcSkACParKdVlWnG!xUD*Tes4!C$Fzc=8C0+qgIIf4!?bc41+*J zt|^Cl9%UK@WSe#l-D7&?ROBhbW4*@U-i-~7hpyFc&dd!YM{RD2_SR@JIA@BKyK>d( zHHQbXI9(3g^!e6hijHbb-uIk*@xbf%pmH@Na-YNpS|@IbHpsK?|3+jwtw=re^z?9) z4mquBT4hLE9GtWTSzUrd;z7^2i$*AegCtT(-=6&`1%~iX0>jZaTQ^4Dh^9ZulwL){ z?sHVy%~@;_akUN3q6c1IQhPa;ZF%y|g{%09TJU6vE6rd!WXo`#5BQqQH8csSA6T0_eOZfFUtKFbI{9ZorR7&CkoKp62a-$|%wLGIP% z>bUmlcHbiy@aCd#rmgdfI35h4m1)P|HQYxeu`m&Q!;}+#gt=Cf0(&(H^L6?v> zjQr7DGD}S%Op>XJT4-I7K4cy2By#WbYUK4NCYQO?tse$7px<0La7e|TedwA!;4L&t z%$d!4K1-~69oxGP@+Ts`MUkPN|KCq9#tk_p@^)qQYOQMKWKn{c5L^ZA0lE9m2QC*n=={8ewpww8<`F#te)>j4>$7d=K z*a}MGVxVxzU&B${KOA?*etAR!)ITF?QNBFB(0V@SZ`~XMCWP_Mt0Lu#~fNEdain7{rn5y$U+{ z&a}${MTYa5K}S6+y?Lh8OqF z090*6EejUBc;FByU3_u#w}F`T9qD!;I0$3o1Z~c4EtEjl!n5Qfgbt%x4a5LnI+n`L zPqe7yle}?oBy6{+b5^n!%a%mVEiApnL+i%HeG(VA*Ur6;|M~}k3M#$+b_C@z+odg| zv2nH+WK=TYE)0jTeDQh+-C(-xGyzA(;+*BXd6BEBb7;6)0T-Wv{P{}q)~P@Y9{|8C z9lB4cM1~x5OqlG!{aW&yW)hf$Z}HHRc`tDD&%sVYu_TF zA{6l;zFYFjlus^-4(^pCut zLVJ=-#*=dY^z+O6f6e&Rb}8s^nk5gVMblNDO~#LV()0)WI>V&OsrRG zN1Gv%A$`Pp%N+X1+lhbe@sdZkTb9NB1fM=U&HZbP4>wq#(<>~ZF5a6}B-%B{3&{9O zXi<0Uk+qrrxFOeqoI9AQIkt%Qx4xkrge3v~E*qDH$I04B5e1lznSv=}%>hM|UhGMf z;H8MvSm#Exy^8|=g43%Va`bBO9}MROutrqwM8LHLxB{Xxf3O!@PpTQY4BRAbe*GqO zU9&hk-G9GQ(9|P${Il|n%i)a;cLtU4*waRH>&#o+ki#R#-50`9CId~pX^EQ^L#`yx zmP>W)Rq_$}IyG?t2qM-rQb^ReaHx?etVKu;YwT!y!DfW4BBhj#FzPo3dn?8WS~Su& z9RB4dU}5Z3V|YCUXVKdryaS_gn8FPuS!9$z+y$lB3Y}RoO6Qm<+Uyk5*h;W{%MBZK z{Ijz+wfM9o>t@^?8$|=&o7P8TM9O!pHpxII;tVS>7ZqKq#;~>r8}ruMJjr;b9{gyo zw2~j8V$`uJd0lmWU&f~O6(4UcJt5i4ZvrX7yZ-kg2anz9|1tO0VNrJNyD%meV$dLn zh?I1E10s8Lu4m3;#vVLYhQ+_v5X*>SE= zY$7T-8tN@d;2xVg>~oxQE6qDXCfRG*Yb%LtvqW??TueLQA|34i9z zf4a5lxb5{<-vNove-HGVZ~PZibg&9f|4A_?$4$S|oYd}3MJA+~9ZxdygIsX<)~0>& z$k7tR`p0z8FFH}>hO?%--lK$`3Ex%}6vXkld-HvjApp)s2+e^Ylo2g`h^}euP7%(! z#vYut%t*Ye|Kw|Ro=?6cYxaSx%JGZdt>N)}{;cW!mEN+?w&+E}M^4!jyva$n03b^W$5gh(xq2=AT7P+zCW8Usoz)OO!%?bA`P(t0M5Hiu>#HnVel zDI#i^=F3Bq7)PR%a$vLg<(JkP-|Oh4rXaaJ##6_FOoAagYm>+Q9J!<7Nx_Irp>#V$ z+C}cP1 zz{qRz8)AR|G=J5>e|mG`=f}CQW|RCf1M~DYk`s!5-;F;d7C@jWEAWkDzrmQe7M-QS z58|#rjm&?E%l`cNFVf^6MbE#&*7rZ9B`B2sAvZyPAaZa*|IcOoR9E;F?EU%BUzY8M zpJSy!$(q)U0m9IP&0C{NrEYSje9;=gYt$g*;5iVe;xlz5xTTAJ} zh8Zn(MCyfq*_Q)}iOVJ@gK)zXoEXSkqq!ckn;H)|2T%)ugGI5w7)(Zg8(MDxKU?QE z|LS>rj*;+JJ*;JH{Hs%gTb?W9Hr|c|;yL=2WKpLAW<*(CTIvqgbAQ)eSaN2=6Gj-l zUfB*rFusl;|707Ga{TnD=#p}6%*UWdO`aqx6yL1~)?JrU3q24M+j1Ik{GB;YG#P zxi>o5;){yyhOP2d4|+U4GJ_tEpEfHQ2W!5^tXpFC^b`gA9YDp)nisU1FMTca#cvq| z!gFxz9@96O^5MNBnE1_xLAxP9(9xr|!A%ye^m8IYfepx4iq+?U5)MzXF%?=afwneX zMv{CGk}nJz-%b=P$#}&&{cu_#3#T5xLcTBp`R@uDd`&QN3?nl#&7Jl z!wZVe)R{J7(4n0A*XpolncK_VTP3C}K2s+%2MfFTPdw=>7I7SB-(?vBl(SpOA`X2P z_BkWBG1feUc6icP3Y)wsgrn4M&(>Kc8GFNP;;qB?`<+ zp9T|wFS3jj`loWz+sB#u+LqNdnMyZRQBBY9+4?(DH${OlCJE-(i+#U;JSLZBq$Y#f z1Vc?80%4%320T$v)uY+7DGJc5o*N#=i%%kvuYz?|Vka8Enj%(Y4AmU0Sb5N^PE*4W zC}sJH9jC_@&HLNQI{o#(&MPl6FtEJ?08&_+GM;R@wzZNP6c2slB zX>=Yg`__=H7!eHV+^buzZgM%I;{Ntv!_W3owJK|D;*Vu=XQJU@(-FzlNgcZ^U+fOQ z9Q{Vf*Ly49)MQI0Y)_-OwUF2i9W0nx9QJz6-2SedPW0L?wmFmsa&31>Fq2${Z#(=t z*HcIR5^vW`z$py#Uk|DcDAc>HntUyLkml%38-0AOl3*@ACQ5kyIbT#Brox9{BYex= zGO}j;!&A~NA3e)g+W1;NX6Bm-*L6LHtHKOt^QQ!i>epO1DhI`qZJU_7Q_34Ea+`hp z9B^g>yDr|sZRXQOP0uts+kwwpe0S4oy3w+eO4S>R-nt9KF6LpEZLUzOyLoN(8B&LC*-xcIvN5qfc)R4kN>8PpM%uvejXYH;`3! z>a5C5>}f&$DQxkT_2^dRIL!z;;rS8k)0(p#x(_!7XoL5Tz2_R2RZU3{T86GqiFL$w z+7rl`^n9f=;f&Md-|x;_z@IZh$7!q$Y2E65qzVw;*Lhfmkxi+7ScNxaNd~FFcvR$r zF@TF!&r~^??2MWN8k?w6pc@bKIJsO~W@mnWMng?w48kfdB3@*pW4P)ghZP%-#V`*; zHQfYRmGtt$NTqNI3|(h`$50PVn9qgHtu_-yX7XoeEY$2AI<4Xq%Y{cKP9ykw%>>r= z*ZlLfO1L^Xtvq=>hRf^KMZuNGZ9b{G;D0UTFCM@FfRh3^*0{!#q$(gbakji@)_hZL zl(ISMLR4D1x~zlza`R$3yim9BrgHq-bZzUN-rnil7WRE=2ZDvzFsW*i=Eui!Ig1Ym ziuZF2g5TQ&c!Pm<5bbjE)Scl#DpfR+P zrX1@NZPk;coQp`yb#Ruu zrk2sOwEVKXkk4Nm5PAVmOdEsip@T28JA^4J)JBd4DMxDbit(o$ zqQ}0OAGBRM7*}h zPD<5b;0Dh@i_JfE$y$y?h}o412zam*axgCk9q(Kj)#ERe(lu!-HpQoYQuQ^8Xc-$U zx3e4B>G)h`1WHb^ig|{3aTDp>>$tBgW?ok(kxgfk23$MK!t%WxORPO%w#J5)iLo^= zCRU$Y53H@muC{mM4oCkU-;+O>fvG;6Y``7$-k71(#^Q3RAoY$cAd8JOxbS|9hKCEb z_%XXM$L{l*z+*IwS7lMuF$dbX9{kyNAL6tl@r>J?;{(X(uCI2tyN%7-)Lg=$H9bL2 zve0>GtHFgaeoG4qk`i+(5?=AVUqL=6c_4KSv_RWB8nIK7p%5{Z=V%C47|iy#Ac@Cp?YK^ZvDTZ^w11lLQOvq}<$4y}V^NQkgelgmPo91(I<1|ZW*n!h((URiUB9PeNgZPePGUKi z*CrC;d)&pI^{;%?yPKHTTdmly`(WA-V9NZLOu4f@uzbD9rh5K(>M$D9N5b~hQ9ern zbW@$UB4Sn`2a(~I`#>Ln1>t{ujzuQnxDj6p*$I9J?%Dv4R$>tgjlHLo_kf%z5LZrf*) zG6~VS*ZVZQsMgAUGeKx3rmoIb{e znbIB4wH>y|q6A~lqCOU2*^z~V{y%&n-b7Eb-p-k8q%!v!BeNL@JM*RY2V*>o)m2m~ zOq9~w7n;5nZERI+n>+h%JQS{Gb$Y`#tc}Q+%xy+WBCdX;ru0(__}~mCrAp`Am9CD< zy#jww+n6@tUr-j_KIK9nT=Swmge0vNgdW#`F~79n`T$BX_Xr2K8zDK4X>$ctpngy_ zdPb`&TGQVLid)o+gCqD2H#dS&QyY`e;I#d1obeH_0{qWp>q7$Zu69mn*=Z^sD!5c# zH@rUwAq4gsUcb?qV-f+*>ze&jurU^`ua1cf`*oF{o!KQrI=m^tL36*?2Mh11VS9hn z3!nk{y_Lq~on?C74c3Q|YzUffd(+Y+0~;h>kAK#$f0UX>@P$k?+DiB&|fc@J+yYqR**=#-WO=t(H;fJMol908zYzN*&9YzDieu$?3oPW&&_=1 zw^2~AJNY+9H&HiE1`XKd4WAx#e@a<0hPIg(Wu zSf#x!@@HE6{|+e{C?)GRrG+tQcjkI5tr!NhnYWsyW06iOk^z$roYb64D`dBM{`IVO zywlf;*syr^yRN97EDJWZe$?Bs{8mNoEiWbd+aU04)#CENUWcD$5?7n3!@e=9TuA6R z^zzl8`RZR+4cX8N7cw890mV{#4Fsg&7N{R}JE=&K02qSg#b_9TnX6@VCW}+BY z5_@!q!6|$%k+aF9-OaJBdtW_CD%Q+9o0Iao!E;awsq!++Y(=Eh-#MrN=RrWVl}^T` zViA2m5+)^wkTJd^>tX9uEbBMJ&IxIHk|4fz5SjXZnQ}~1RFm9yJmB~L=rPb>KZapK z{Sm5l{tDG@GKf7qG3fN=rAO;(R!bJ!T)(=p=H9Br+`8?c?Uz1FuDO9kSGUa_j$M$t zY|1ASX?nJ9##q&zuf8SRLNJ$7n*5d;JT;m4t89I)v3yj^`rhEW24`f}`}mohtr_FS;X+ z+qxj@L+1&vtA;YeApXyB5in7ZkF>F2T%N4+4+-dES(6&=nNRpC4^-*cu1w_h?hTY` zTicHB9C7YarlY!UVqaHGsfTSQe!IV)03rFzvgPN$m6p=gtdg(A8%BNFXgt5MHqx&V zAZ(i=?>;Vg&s6lU9zST2_zgR6$CC7-@u@c=&q@S zRz~(q8xCqNx+LQp1k(U8%hR29@hv=cGSCblNOvsit%p`HN3coUTHKh zuf0K*V@fMN^wTpjen$yojQl=-Id2|Qf9MEl_%z|j@zCjvPYMY=h3C8WJG~poZ+`Nm zrycrcJXl&#xPBiKwYh&P<>vyaFO4>ls$51`xbt2X_}vDAf&?s)y8NK%!JPA=dDqrG ziQlz~F-DbnRR)1d9L~fzV*iO04g#bw7l3(TWsm7p>IGb0kp{{WY2H2blMP7S%IH$kEk&U#!*@Nu*Jhk{65(+B{rYefv(hE*Kg0RU^7 z)NV{ALHq}Y-a+O5+j0Rw_>7Hj=`zLdR|JSRaUANBJ#oyl@7NV7GJ(q_IdJ6J#uL0vSS(y2;oSWO@#%RrXevCcch35#$Gpk1z@baWT-<@)r;#gD}#m9 z(zuzJ*R0A^3;nrK%#LhZCfBP`uq`;#L2GGL zN6S3=MgoAl)RkH^Bj_`Y=iukpjM@hrjDccPNuS;MTn_zW6ADVoZ1{|;+u~4!nb-GY z{aZ);gSA*>b682MLCfh``wMMptt@+=s@tA#e&NQn%r)g)H-%&`HWLe&Abb1AC&nt1 zfS3@#ViIDXG^*G808^#W@sCj8`vL4^lv!f?^<9S7`EMRGFpPIg@?HbzAk_pqDFasm zEZx-xTRLA^h&q{NI~WJ|H;hE6mflk!yan$w+ZhiuBaAd|-FiaTXX)PKhui3#Y7LjK zFRO)a4eD-|a00&tJCuOWe5(ukZPZ5UhSM>k#dC19 zqe;$Qee<Km##^W&aU~DYu zC21cWveV+G>bk*^y!}ko1d93LYf$b!T1maWX{l7=tTU`1D;qh8F~}`}ycs`2r&V{B z@))QV8UAtB=5@$HbyrFJmuou^U3&)LqSls{=LA~}s@n}a;Ys>{KqN@ks16kM(9V4Z zLsFwu_^YQD_gK!QN_oEm^a_Wht$Xfi;5+mKG?{nQpplBV{u}jSy(yQGa_d|o9xZEo zCw_3tAnC641M!F#tM!jm2JY58GC^4A!Zu@@P{a+V;rxk?-pz&E)Bd=6>NwLbr4N(2 z+CQ$rEMTl?hqx90gR#;PfrCld9k7N0RWwP+rjJZn8M$gR0_Y$-Rb42#0#rqOsUcC~kbo#XA|TboTW4 z{uwDY8G&v>$`0dWi+ta2G(Kz(i;UW&`09cZ&?WE38qNk%DGpDuTPZk~^p7|LIzdO| zt0ZE}n)umcnAQ9D2wOGggxN_EU@4r=p>qLJe|j33rAWR2hQWQn%L1O3xlZ7VdIh!c zkoJndP|awblBkBc&S+0rD>P^H-QokuNJ! z_vGf6zPjhZ9r=xvRYpbzaEZwKEuy3qE=joYb+wAtTQ^i4mZkW{j2E=(`qH{1Lw-p@ z=f&A?z0_bZf{w+g&xa2mrkEpM)Wg2VL!jA@ldVHD)`}3-T~wAT<(81XshSg|%S_a2 z-SNZ}=*ltBI-}S63YV&fZocGhS*DVsZxj8iR%wW;N^qqXWk5b}evu^#2mpGlqUYHt1Y8(c;1NBDgaapeMY`=rC_BmhC69VbWo#5 zOOH(d4L3~0{~DMuxu43Zlb0UX4wvM#7MJk!j$!L+Zls*36f?q27_+wv$ITd|lVUHB zkQAxj?QZ6JtSNVQEdK1jKqU||!vv>|h#IvzpP2+pbB_OzJGF0GB zuOt<{^e!#SC9WLoI|mB3s%V%D_59{p0j=^D*|+gC&fu-;%7C=ve8*OCRna%0G-F1^ z3fv&*On6a0oPW<_{E7IWDl0q=Cz|v5Ibwce)?UsYHw3(HN=oEp>UrOwI#?U7&{?=^7i ziXISBxA2%Nfwj15RdOfS#4-d5so9{v+En4PCBZ2H4NVequocue_G8^HLwk-^leI6J zeO(eg#?mU_Vv?TT7tm}tGjIywW@c%VA+{5;DXgt+@7 z(wae|bK5Eu9G1S8>SQU9)%oGdjM{4KPz%@R6pgjW^Y_kkkmjv?JY-(K+>s%~E23hL zkfyj*Qo@ik)bB#rwcPD|Hay>zeYkRIqq}hPHAHbV)qTLOOBU0wu5YuBugl>N5PSFF zroU+-OILArjn@?VTb^oAA0C(tl8(4nNzmF{2;gUhfXi>eib1#%sCkU++4X0X5XA2_ zSqn15gKeIxp&6p1m05YFzH|MLVYH(mm-SmQEBiTWqOZBMP@8tF$zHyeksXFTJ*H_h zdFW;kAXPX|D?}r|j#oeIz18gtMz!QzQ%%zF@sXIFo$Uo9kp~jY2k`s*^R?)-tS3*# ztc*XH3S!|vLU3(C-o5$T!ZDu*CiJDL9x@?m$y;Yl_vepGOO}Ux6}9MEA@LD?m{PnH z^PG0`=I7g`%iJhpTZFRP&KIduBWr2QdMRnpayt$l@SVkMwKIfdf%;rvS@s(glCr(Y zWKlHMA%SRq7*hgQLc#R+&_VboPvquRJkt2E{(EE^(^>Er!`-;m@of4CT1m5ravXMf zQZ5yP?5?hNR3#Z2bMkx>c}cQA?z2NMM($$^pfs$kXXZI27HLbYqJskA z5eFn%uFj(COrv4quj?o-KpeeK94>+6k}~;ebo+v$-zs5wX9WRMKo8cphVM)!UXr%vchH?wEh<>4Y>PVrqQrDrx;|;^f6l7 zk?3*l&6$~Px0O++tP33<@yYGi+b{$KWj2j@=YI0s(2+;S{@4?{7>;fP`1tAe9Qn9- z-(AZOcwd?XsI;B{vFPQyN4A3nl{Cf3OnEM&ddH`9)A#XmAra@uPZ%`3y@V<$+6JRa z<$%SMAY+soco$3}YmjGL%}AM6|4>ZyXl<6R-B2$E(R>3$da}CtTCsR)UR{eUg{W*r zM(^9!Q{6vzLq780pnS$VjaGynqycqc+_1cf-TRehV!-;1_h*W+Xjx)n)9wP7_#1OX zmksr^{;49*od>-(&MGdww3RBI_)qsg8rbFwH}i6bB8yf!(G->*#r?Ea@Y*p(G61{JE+>BU<|JDy9wQV6 z#thAM5w+E&<-r8yEI^ zD))w7)P^(X=x4w>Xix|3WNEvBfh4Io;v$5|X$~@y3ynKQM?`D~*ufMVT1zXdF!B9Q z!4qcVs`>G8abQ%F?nQVfFezU0JENy}!zjNSHb^v-UGeiXD=>IV9O9J?hk8Slzl2JE zyeARUxw;!+*9O`d9Z{-Xe9X?i0d62*H>@94l3d zO*`Lzcm|~AlN_yhD1IG&5K*0u$xa}GQQ^HntB!~9OjiQGob`LLRkBMGuL&@UI& zrEdNePYr)9=b}2B2N;z~c<64> zzw%h@=z0Xlul^BD6((F_LoDiFg~#!1WUPG&aQp6;M8|c~YFNueYTfnGK+Y|jfaBmy zSSsJ`g5Eubft&*Ft2X%{CYn_STkOY*zzy^4=#Rcj#CXV|=jd>QAwb*cWw74=D2BU)oU=ati#_1K{R!!B2_mK(>Db ze}4owe>{@7!KGLt@+^Jh@;{#ow?)aiS%?8`q$EHvQ0h*uej{u*Ouyh|l&jT0pA{vW zJz5FLpoaK`*12tEKkqgMVxr8RXBs8lnLmg9_LqSJv^d+M)ooPC!N>kg4c49gv{dn+ z(Q0s z5W*O7MwN@t9eA{-?1RzV26xElcQv*agdeWG{Qg@1QDmPWLJM#XIJl=Pj6|E)8rYRp z5dtZ>wu9(-d$p#F@e%LWUf*lOg>1^0gdcth1gV4C5!t(MdWUK8P;- zs2r8Ys>&`=!)`SCU`tc8BpNsSIyXOrG`hn8JCSqs->*D!9d<{J|23ESr}4_NL_+%&>2g~Ah|IJ=Dp^ZhS!JQe7007WZOC-I#nnQDjQA%gHs%)an z(k^VgF4bk@6#Jq|Fb{v_-=;UumNn^UoZ^d#-%K>k9fyn8MbA6aC zyy=TwrnVzn8#u&c?VN0N@E8BM6mPDgU`>s1)_?hGG@jR_;wuOcW7CMAoD~QCiwh%G zCojr5RoE$N)^C5wJt8Yu;iPU|hG~%?DNYMvx_OX8Ro z_l+Zm&E}jClQcdhXj0phVa9LbM0h0Zkr*0AePl+IX-(%uac4MjM3A`gl*D@P+=F9( zYe*%_GlrDfn2CHe>CzCdf-ZH zC0%Bv&K-^YHNtA`DyuND_|EpMQFFfiCp`5<@Ja{nt)$a#{7pVh0WW+!x*T)koQ}jF zho2)9$D4nTpnT}(|FmeBk^!xz7AqaS-j`2sO8>S!o-x3X=J}ocA6wT0=%7H-lCTO8 zOlA@iqA4X9lGpDww%Stdl4tM@+bQ|A)-~7U=zG(@Wy>#hXTxyVXQ7X(RYxfH*W|7@ zV$JyWcNd-b1v%iQoH{#kX?`wc%b4P7|E5mcBDs*tQ7bZdd*2Yh1-eHCfV_c(9k}x* zlqeeHgFRb5(P?*uiZ)Rpf{r^w7Jt9!`PKquK^sFx%3ic$n`X>4Wt+WiH`6Z0NORWv zV^wZ|hjC~EN_^;pN=&3U_eiA}puTo>UhBunXx@ba`g#;&fLBqCqF+fx!XT79h+pJDD-fCE$PoR2>LpvCzcqX2v z=J`@;0dEO#a}#1^W9tW)&Va?CcM^O=D$+l2gjmXMVqf<9=8$pWJP=hBZh7#UM)vCG zc7U!!n}X;>3Cqp*8GLu=ZK`A|!q04(+Vr1cg0)uI^mE)_2oEdn{gN@3rcXwk?Ad`^!@{LZdta^b>8&|Ngb3%8+~ ze7`-{6G`@}aHMr9X~!DZ&UStZ{mEg&+HTUXwm5RcnYA@}6hE=r6ce8E5gW!7_s+|> zZ30=kaROcTDTKU#YjHRT3`gdWlpM3*r&R<~vA+dofahVz)#-6WS5QixDZrITawJD1s} z40^l2zUDy3d}nl8k@gJsYkL3wqPg2+LzVC>1Lyexn^c-i$2Oqld%LNLzGZFeI2k@D zQU9A=boP4|=B_R-7N#s|9@g2E8ggLDpJ+pi{8h>MP zh*T4m$RO7^b>iA-PKQgXcYs)Bd_U;r`3zkbkno$yqjTNbL)l8}Z4%@tXc%nS^(km5 zZ1{=S@nekm`q5~xJ5qnP$byaBPC<+J8T z0Bhs+A^N-J2~>*w#}wVW%%!w$C5kYERxRVXPvml~N8&WIJT#*iAG=WYi_nXhsQB+b zn^(B^IpGp^sa!B9y2)pYZ37dYLB;(d>zLQk5Bb{YXPVYWxZ##F0?RT^&|RBnG>`co zeY4`vmQ#>1_hC@C)9k^n!@m!dTfgJ!8RoO89`5wFV;*0^C=1E9!dN_2 zsE&$jD;Ztu#@%OZWl~LVc;fmxE;?HfRUCjcc?;)cue;$2HMn7Tjl891iPZST;%9!Y za7;g7!^k&{Hh7nJpOBr8!=WeNPTea}^?fG}B;A`5xSFliUy&`{>_9X(|e`yDk&{`oD z#vxW0!LOoijE-0y2ET(->~;035vrpIyy?lcvUtaRl7 zslqIEny3l&(e^2`?YVtYvf1mtNYTkj=;gS8glY}$_-4##oeQm<;sDQRgJofc)JHw4 zO4sU)@r5Zo1r^n`4N5HEgzsvjVYiumaE|m>hIPJ;^z@55`F&^VJeIf34XoPk z-lUiGF1y1}?=tJ;Y7=V^K!^eox)`oa3{wfUhLJRJ<>Mk5`*FxkadKIjo&DA3i{!>$kLAm~mwJjr$54z8`abjC-*1Wl zJ$Ff^mRX60)wxBEVNtIB70fmOQ?69u4xNCS0b5os;Egn+h4L)pydaO8%Nt0p%APP^ zuaYABaNq-;XPNn^x9l(nzPgBm2x4F{G1HG^WU+XRoNjgqESH8s!tGAQ@`4;WNkas{ z%YMsv3zMMPXxV$MoR#+LuV!B-yjD3TkLD01G6X5!*7}-rLb#_fQ*kOPbP@r&p($qG zuz6r7X^bj;QWK0`#z1Z}Gt=8B&xpuLpLo?u{-Wmrny!N~(H~i3KM{OcA|?iXfaMYz zznv$=@9TGXC3k{Mfo$t7PmlVcLx*e;M36P#=OY4ME1xiqm7jWru~1l1Y8Btf%c5!_ z;9?JtJj3t)Eglju*jiJa7*fDxx#4WY(E7kDuewSxi5fpmN@nF+vCIsMVhSA37bk)) zX@R1Zl9U7ZMGf>FASU#`g&e*HVZCIvcom#(SDg3$5s@~(7kKYkf7)`9kuO|}ci$xr zRec!icul$z9q&6uTkhInxzW@pu!7U?VZ-7}jAAcG_S$YOOxvaa8`PB??Ta@R{t$^m z`(8mQA!KDAGW57IQT<*pkJIV&E%=N_m_A&|Z2e9Kqq z<1JHrW%>9D(RnGA9Db*lM=!ozSsiUumpT!%7)$CLV2f;O(16gdXcO1<_%s9*fuUpD>#Q(*m?tIqQ3mu$)4`U zD?X*TLHDlB`Mp++9l4M${o~eMqk0`)?9wH^?>{Xn+f_mjJVbA=b;~2{TjTx~lf5iO zFm&wcaffWezG&!q3Mpu_a{N|b?}I%a)iVG1O&AOre2y}f8QhJ|wL$%8^Me{2TzYrz zw(})?U{F+aiT-3fzg>*tg|+8r0utVnCsa7Bi6AD>i!;#5)AF{9pFjWjECJz%5wmE_ z`q()yGLmCv#`oJ^99bY#*zC0yvQ*aWN$N31>!Z*PzJ=TI3SA>9P?7bHr-!-6I}zkZ zzm6jtytOMbx^T#9Cz#jQ_xu5pf#cUm^hkaJ8Kp_M_|6K6*jG2X^h+q^He*AMYUg%q zOmpPZRyfMN^Lq5|Uaz@?2maZoDsLYWrjk33aNc2hP@U(@mfMasZo~T zoMIsLGNitf->}Dgbb9$dZ<~|Jx1dwC@eMAshC)!nea#;7BVOmFL}sPxZY7#!1W}`= znHNT@ynAKL!XHBn;aOL9&@W@NS80VM9dR33AKw#ki0Pp7albxBiM*k2p#I!&o7Wxy zj>BNwCGQY&tCjqQqX|?8CP`Kbh-2WI)|wki)>fhFEM}s&nE6X@ytX;;)(syOl0s(7 zy~bkrMOHd&iHV7c$Fq9cU`!;RA2sO`9A|KZ2Aa#&tuwQ;p`;{!JU!hm$s`6KS2{qN za>1aTpws*F_m8-MKBF@66DoAQmNcj#D2z4n0)nYtLHmTpnZ}q8MH=cTc-_GR(m1?j4G{C+0hni z*>A#!Pj{e$E*mz#-9{kc(+jr5n;-7mjxX(5ZSRY4x#|?pwdCuR*37=jKIGO{#SQNa zUwMrqeQyg=^&=#KqM8>l5UmgR_`{%n(-DKSmP0gd0kSZKT&j0K^L9eb&NE`8r+*cn zLW5ZPh3VFPt9_@sK&`ec(i*a~J%npbJP-N1{U=PKDb!E8T99yLlT-P_^;ec|Vk>!c zf_qJg{>|MT46$~VQi0T>^^KRUykA!*K)pX#jI7y^tX$pUIZSe6J@9AI7NBQ9hNEJG z>Zkb0#y>K}-w?6`qSFK2-?G>P`kf)*cpo4WYghlCV*Q6w?MvPN=_8}KP|(5M2*95) zWBUzDq{Q%ItAM7n_tW3u#~-!zfBx_Pzli34c;tTrGYwRU`%dz5v_~gY!9-Se)@!Wv z9jU)%`v9nl5dN!z{5jPPw zCYp``=gFA(U)c@^1lbN2vsBfpgqF9b35`T84CHey91VFE zjU2GiY4maT*&5ufaDexi4{}R+Y7QL8(1Gs7)Z+o$;+ANtxzU3Hk(uvXaoxAOQk8bt zIIl+;EfrdkBMp{Zm5vi}1v=hoK-FFW$QB9vnVsO|HOG|XX`^|j4ncZx%E3NmzkLcW zaLtCcneqRVkt}3A} zTZgHR7XJo2e$xRA;?K;6e0Bb zRg~*k^dhDG27 z5mHp?z2UAfF7P2qqzU$&CBMkx90n!wp$ta1_nDQ zF{>v(Sq-87R|RveS->C@y7!*M2k2PMeGg&E0qBnAg9mQ_FoEXcDXy4Ry2Ra#-{tbJ z@~+}bDf&^McObedjN%w<>s|7E#uvFlG~15tH+r~nN19tvU?nIC;*}R$XLCNc9xiMF z)1wytLI|bM(C?Y8!7lQ}n|QlL$x%;=7;LhT184B2-SqZCQtbMt!{Wuy(b6H$g2LTN zL6s(!+C~)$k4$~IADph0j9D$d?|Q6Ugfh!1Si}Q^qLiSje(~|iq-YCZ@hR?Mahe8e zA-0A#m?2}QUN)HDEpc&iH{6EhXss)ou+yZ8K70v7Nv%KXk{uaGODdw#jq`uztls=m zZVL1Y_#=X#dUwcvfm%V+}R4PxzYV%YJ)vg-c2- z=a^}&x9UW*ABIy}C?~rP@#j`b`I*HHn#&U0QvF0xT^-?Ci!{*AlT!joMA z>~cuhZs-SQDPnoCM4rChdHTX78KJ0KA`RBhDRa@tB_oF3clYH06O|kQkDbhvzuqpE6>P{!t_TSsSS*3GBK^ZB#6p-`v~V4}7(#_>zSSO{Xp z!jxu>c`MW=jW>YE1xuCpL!xW>WJaOvM7p*0_PM=N%fm0;ta@^1D~lGNTK9lgA`6_` zdMm}k5;eycb-pCo&^kq}Q-qMqv4!qQ*$xXVoEOt8K1@Oq_gCx-#wTdC;A{Yi`U<3! zf7Z;}vr9I4aO&Prj>k%qo)v!@7Fs-4E#5b}F-uT&TRc?r1W@Ombc-Tz#62WY4Z6Pw zX?+@)dBwk^s9dB3(#)fv%&t>p5Pjj|MPXsbPYyxB!F>nwRtyadT@1c0`3Xn?%~6PB z=&}e`@bhIPboh1z<`JPyStiRVld8~CWhK^C$2quURJr>n*X-B3@Cgo{nq>6#F{18h z!jECYat-hf8)I5P(efSO{{MyOQlfL;fuXD=XBnmLfckuiF^V`0F@^Z4h8rfYY~!6y zE!#b>Jq1IijWbJ1CLce`@Q`Nf-cV1K!`K`cbwPnOkfBzu|3-S)>QSRCh>AA{^&#!x z17i5Y?#6d#XEC~?{;cP}u%YBLNX0x-k0R z9Lz{>glE|~QwV0&0Pd7k0cC^qHf-M1eGv$c2I~H!lq)7%>4Y?+Au>w)Xn>x7G8Azg z&P1Jc_`$fai&0mk`@hTWt>+y^D>A@jo%N39j~{RKvLF^o9`e^)N6GB6J51Dp?(n*+ zfD`IDkf&+BdBe&oCqa+TKEZ0ejyR~Stegdi9HG;efE(Hn7O;HGzm;?Mx|t@#P+nGs zH@2WFAl`9*qkJbGOSwa{vWzx^uIw|_euB^d4^!MaVc=Y%{h>>T%gfjP%Meo@F(=;* zP>uM@GCFrxXE_0J>f|34>HjO}mdu;~Bf2GJmrCW6YPc8kr^)*<^Sz^Yc z0*`v(FOVOE<0&vPb?2t3hE|-i2r(^CHC>wJ#5mk|B{ysF5yiAV)5_OUIUYbK&^Y@m z4fx63JV;VN82^6a(BF^hZy`IN-s-G+_~IstQt!fyH0fINvf9W@PzZV~;W@7mG95{`5SwFCTRhc~S~Zb>`et@M za1UDMT4fpIiGPH)|B;JVkK64VM*1lN48d*Jo+n7ro)TqJY^`3LVC-;727XXf&skm! z_Q?j_1%=mVxQ7o-_}lhovR2yk$ARW}RcqG`GH2gEHumoYMZkB`EtFaPLPL>Oe<$YJ zo+jX8=7GjKp|x@Zhh+7QmI~?JNR?uSX^d{Mi7qu3((rm==&d3sW4rj6>6!Q)-ddOl z0nIgPyp5Xz{%iN0lsd&6e0l&Dm8X_kC*<2BB`o6f;g;>!FK`b(GCHXTVzZPf+KXru zb?<@D=)>nfnGk|@$<;^niZX|R0?~?Py@ylphp#NQU9o#dZl;|}hROpC&EZ+< zhg884Zz0PCH=n)b$T}|sR`$)zYoU0=*gqHuXUS+KQe=ozT%W|B%86=rCsneui|J4= zj&{eZr72NPEw5j^mcL#)eeHSK#deNcU~SpX(`O7`yO~Z-vliX+Ow?hQ?oz1o zmqFkGAJ?sh)yDe1_nw4;fZP;cBP2R9;%&L^c9>B--NAJd=#IdlprCGQ8rjMis*feV z6v@H_Q&^CRI`O5gmJLE552U7()0Z$u0y%ZlMBkpiX~{9N!(}^C28!X4LIe80Mwb6c zac;-k#U{mZ_zVN8Pf=WHv_C4*I*3!(N8wB-8X8-}h0-$)@m;9Zhxne2?EDm+n(DdQ zv~TWkIPF5Zl+L%hQIU}rsH|<#{MO75p2XYK;P+#ZQ)M_&JET4RlMbzaBoM!EM*+@y z{heQ0eesg0&8W^9oA^ZzE!3RkEBipx`AeQ2W-Fl(#&@p{C^CTR!Y_P7WJ2lzGFQ_g zNyI;H#URoKDe}G%&pMAAuHufbb%9>~e6cTy0D#QiU9y|OA@){(T!LTL1t1l**cAO8 z!~sszMK5go!hrNfDCUZ{6fw11AlZZQ7SRkrJ|Kq3_1qqnc}t5*gF2SGFIMF^%lGtj zhOe(5wrV(sES+X+nSxs7q~!9gJw4i--|xncT#+6SCrm2Vk*e(Kmux$@=xlu@XL=+BTi4XGSoEAC5)8z^w8JwncOL}yK`ky!T2xfKSV8GViZy({OpZM1^e)ceI zg?MCz>@_Jx`7Ow;UT3&#Xst%y1?>#H>4vC|c(e3V1RPK||6-PfVYI`YWYQzXMEa}d zOb4t`xk6XGB75*9sxLA*%aK7p+)?1zZ@z8lnY~8HKAa&Uo0`o-zZ*!o^Y&>6T(a>T z()?P|Xy9xM6KQ6cMNpy3NAv+_px^m?u#JwV->o@3RZ8*8js=^LG746;5^Y`c` zmI4X%N;Cp#4`#$O;}ds-U5emr9#>x#{T zM~73ZTG!5zJV~E=j?qqD-Tji|VEoW`)6vJZhkA35cOle|DmCDBTFTnB)OruY8Hr!- zq>RA&hE5VWw5|K_$CPablWv@vc(_;3Vq4;MB0T{Nx|DQiBYy6^TIaHL+%y=6J$tZ@ zK0KrW&+=CMwLBF6;E3*AW19|xF}<(RKx`EDn&EP+uUoC1S6?zAT+h9U$6!IIgnGI}xc~zh7;o*rag3cHgH2#&Y|swIv2Eq~c-tjh<(! zd>xwM{RJt@3#Jb5w^`N(ewkwTe-tvRk2?DDTe19{=}xrk$!b5-lWNU5+ns)8eQ`J$ z#=FS^JRgBs8j0%r^832OdglZ#%&<>RaB5q0v&5l5c!4GQN}K$~SISw`!ZO`W5#5?? z0N+JjWU4)`@@QbE=tG;<6bU&PjOJ1S%V8Bka{UR=W<{{^hz0F|0w8Sc0{#-O1*vf7 z$|IfT87k4cIiNSyD=_im*&HOl#+u%L%U5b`3;+rUh0c2imJ?*^fvm+mqv9wv@{4fI z6riqqF&8kC89Vcg4WVn&(_bSwx90-QWUhI26 zi%4s$lI9LGk>_7E|9%!}@!(C0_VwnE{K6xSj4&y4QCj%opD5$AlDXlByi2ri&(wF8 z?8@!Arz($CkGH4Xr`Ld7R4DE`10Tg`9dQ$FTdLe(dXmljC<%~m){|75cS(>058a)EnuZ%zP1GZTUNp5jTq- zEC%{MhmSooYd~`6wa4c0cg)R7n^d@{1L8qf!(yK;wk7p~QV5Z3>2E7#|7e;o&$#q7 z3ronXm4DG+7{=XxL#I|`3Dwo=jcpklZTL=mW_c~~6>oj>_EcKkmN}Vx|AkWKMfiX` zSp@OBtn4@Q*;*4z4*7%2jy*ws8w+ar{jR_6F5ol-X0m5CL)ld|S_8K>?(Cfv?`f2L z8hyEV>nD_w#955U(`%N5%T{vgPtcg5Xj?n}Do$DIUHrSwnoeMm@m#5QZ|(t_M%QLz zv+Cip8E4N+ixsJAD(xxT>t}LyN;ZuOe80mWU!9UgE}zai;vwIOCl&2*3g-(5*sy8s z=_Q?2V;FM0nR^kFju04$)yDJ=uEGiJpFl)KjGlNMo2>bq+@b6J5SD3=E$807C-W-mIK}N-(Etyi zYu73LWj($8lg7RQWZFbM@b(zU&JFsU?tl0VWO*1;u`0C|hJMXh>@)eHJ)ap49&^}5 zB{=)QhyGS2Y54>dH(rozH2cP{c)Ir%S zcV9?C0^H>J?YMxC%E36#I7qC@y`k6O&T$pC%o{Ckk;j2`$eG7y{Vbl#^1jv!u7XGN zn}>=wlLyyV?MGP;A$7B7tY}-Wr5X6zr`{TbQiP*7q@c_e|K&-xrSo*er$Q>>{ww+2 zNfFeEe0uA8O4Ps2PF8}p?6H0L+0Iu9_d-uH=rcqG8AM0bihVq{1|$LIIdHI81{z*D znL1~Q6gN5=4YsIWXitE~;sU7BvEw9yGsb(X8&7vos;0 zt{cQzR(x0laTX00`Ovmi7gp6U5yv;NJ@s^Bv@i){qSJ-<=(RB~g@mVH=ZSO0}Wde)v9zAWd%E8&syZ0jQV5EM?iv#37b zIA0=QW%9ge>=&#r`~;BITl~?Z{FI1-P&;{pOeiDeranh^HVl3TMnYE4I)%2tQDcNR zH^@2{n6e69*S#w3d-5iQU^{diyJcB(?I5VVU=RZE8gL-wl>Na$SlQ9xbp+V%_7TcR zO>?!;a%bXq7wBK3MXOF4Yl!PfuxLIQ*bdzO!#WxYrF0f;?;v})z<+1xKR!P8fp!I} z`fO{%8(i=Io9uW%Nlvy@;1n@W1tJ0dD})yv6BD|9gJ!8OiPEoe7*`>o6#V-mp7b%a z=j5#VJHPRmI=5<}IED_>A4=6f$Eb-6a|9JgbRMO4MZ8g;+P1|0s(ymqPF91u2X7h2 zQ)-k;w#Xt%DrN9F1Hb7Rp0(Z}xI}xl@xD~{l5wWzOER}kR9m;+dLT>HeKcWVQti&l z;&L$ixe&7>H4P&M?^EOvcl?&&Z6Va5nUpPM>HcMm)(C$|;U~1km<0H!fQ~2~EarLXK z*kTA~&3DpI4ArfZbJmfTkxPL7z_om64+oYzQVsyLpcRLrnv^ge>(8$Y%(dXEuCh>2 ztX*F&4$>&UMvhFOl0;$-AMWTL$?GfSW=Msp!IZ9)9{y<7Oe_(g+nnZ=O1M{j{A{MM z?1rvZ!*P>h*vr#b&4E++I0qNIB3ON%&Y)MOT{!Tw0dQD$=q43Oxw+HFhUts!4aFmejB_BAhQi*)>)pKO0J2SCzx>{ z@U z(tm>VBe%McqEA8c@zLh*yWPf^m zh0D;-;QUlt!tdaIIol~0t)7e&jmxW?R-NG zsIRbPh?X)tOiXN9IMd;>Kz!%7P(GzsK1=B-95(g5PJO{M@}GlseKb*Z2nS%cm6U&D z80IMXW_562+7U6ZkO&}l*q%MS-SpS48R^#xW~PeVFslryLW2bvtHjnaFWvx{@hW_t z*V)^(qmU&K|2IZBoWe!E+2NctGLggz{(-{*CZ!i~xB|w3=Rt1Sam?;o(z?@4ASMFE z9l`*x@)TowWBG%|83-w5$f4fET@hBu=JoL3EHn3hoI~lo~YfL#U z)3UCSX-}FB)~)F?cW4VdPx8E8(8h5Rd+%vAnmFqk;)=SiObk3W$SFJQ{~|{;q2ip z{NqHye>+E(9ryKAZ5Vn^KiaQ%yd3HzQeaktqh+>mZ z=HgOz_@!uxrc1=&9A6Xe6H4LxD~WHOXeEfDFouaJ<(28UXK5SNe^W7-XqxhMKC3j; z90a*Xd0fWl`(Mq)V_KS5C1T@yz4Yu(-^?fTFV35s|6qFgG_7>#SzOE^J<#u-~8ncz`7jqA%}Ui<$2nC@uDho-1yCwnaG7{pBJM*Wxhh z%~2ZhPV4W^&yoscdvmmP??#qJCf_|#19o<~rS!|JrD@)2&)Ur;&nS?d-{`KL*iZ73 z3#ou;WJh$XlpzsUh1rJGDgC#)${Oc{0l!?F%68}oCuIp>=uqNnLlThj*HRmc>4FR%_IAf@?`~P})#~U+}(%C{ZvLuG(OLmW;0>M@?7L4@*3Gt+l zHJBvV>o;03VBJz1p1FC0>M-9s@%AUG?{$XH1rox>j@~)JuJza?Mu{cZCF7cTz_H1& zPn!0+feT08`S%`h`b=luo*r`al$w*{ZLgho=k1VDX4RoM`dGy*uuVm?S4c+o-C4}M z{IjWphC+%hSTZnz{({n4vnt&pi!;480)+fD;BTs%1;^m4a+bJrOnY2EXvtIZB^%xH z;wUK464WQ5?8#hEJN!dj06-58VAo!j7cVBhv29j5tQsNQLA{XkreQ0T!cG?IFij#z zq1|EQ_@*uO4)|W2(pG=!+E0lq$qKLvLVh~6Nk>sfP%qN!s_65Ih0n@Ar;b+a>b^H^ zL8_fV+@RV$uDbf?ufNVnd@oS6yx$hrE>w&}*xr|5aeJ9j0flAtL1CE@JE!=!K8vFT zMVC%CCJU!M%N;D+@fExck-1YRyFvV@KIAY;`mr^EaqMYb577OK_G1lq(VyR$N3lfnq>Hq%t6_T*oP0St*}6j!g<8~l6;kzln9qgj40>&*uW>l!wMNbo$Wp@LP_Yg`Zji!#&sTL3pJwDza!k?OSUXObFs(F$bQ* zF@PjK_$llk>+fprhk;wOvQ{U19ll@=MBp@54z(O0Fk?g-uOwxdON^r$s3VLt3g`3K zs9k;cMhLnF=igDn-p1fnK%iev$p;pi)u?b5nhA-U(^lRUw4VXCTO5`5WqR$iW}Sk* z;aqD%su;3PmBi%{6lqJ=jwyIZT~9o|7Of#I*@p3&6wDk%8b>`|ow@7Bw;+oQtl)i_ z`3tYCqwT<*z33ghK~l1T>(T5kY_$Vo{y~_ud(_xXP2I=H3$_;T6zz+KQ)s4yy>QiQ z{7c!%<_3%Ew=0tb$-xFqgxlp&45T<6Z0^`xx=%+)UERJOkw^$6Z9uNlEf!^ckGc+4 zjePY;SL}#<%92>)NEplhOc^=KNyUH zB+n_#yC+n-Fdw7Y9rh}_T-h(PQF{3;Br`3cz(4m+c%dE$xA)6!8CnbMOw=n!p2;(- zWP$4D3+$P}{|j{g7_zzafLbTk`O`15R^d|N6S0m~fb@j^Kzlj9v6413l;K_e*Zv3bB65KO`S7Xoz^wxt5P{4w& z{$z1!{I37W#2TJ-uypRysqUT`4~Mad88N%hm$s*>MfVz$wJpwPj=w20KL9G%Rg-^wm%s#o z<8qrkA0nhG|19gM&YoZR!F4zG_Yc2Nw|J_^1pv}FM~BR=6=4Q T(8SrjzEp?5>z~D9tx1@f_uc#5`+fHF>>>EEhVr@7S58w=QJqtH zsHjavMFUV#9a%Yf9Q;HxuxpKqDu7Bw@t&@i`Ev3JF9yBwCc2#=2D)!L4acI*%-jx4 zVNr9vKTe-KefqWGZEbzigb?aS^g_2Et?_@ke=<1w!pSehHy0nyr+>NtyGBNDLh465 zWD(LL#9P-m*4DhogDPF;)GB9VCz4z?>A*++ajcri9pp;mbPTBf`ec$a83*O6ZU-iR z1kxS7cj3>U%7|w?fL4&jzdnk5`R?tKhNA5G*M^PS6xBF3K|+oiLU@(iWKb3(*G}r> z$H?MSTyUWXc^pOT6~(G!f~$KXMh1|ver9DJ|4{JqW<>B7ZMj-10T)vv5o~^U?yJhy zr;g|RK$04uyGeKs8;S=K6#(lFUY&UcQ6ND9_=L#*`cpUiiP{5xhHc$xwX{b~x+zLA zVOgnPKXkZdYwM}%S!AoDY@2lP9iJCrwlA`ts&z8!_QfD{i(_MrVdevb?37`cm6pA% zdr5zfj)AI~?6y_4wx*^Z|MqrG2Cfu4_k!eUMd`GOL>z}*>58$*L}^)FalT@m;XLDQ zobbXX6=h?U;F91x*M4{AuBWD}Mo34=i^2foniK?9(y;=6t}F2>93d|0XgSB%GQ{R4d-f!W1Ifc#1cD<(RAz% zdj&p_Hq7V|yGKjHdDgMMjT>FU26Bt(>6^lfv$42-k_D$(Y+rMeZ&6ZJORf_xN;Af{ zOreJE3W4-!uOmL6cfnP3OkdjoA{ zS@%R7X*%00{1}5ZKJHhm)D8%5ids8aQBe5AQsKaD-$aC?#C_q-l}LBvcec!_)`nK^ zTKKv64%qfVqprBsj4PlWa5n+)VTcOi0Bbi&7lMz};?4u%kL3ggm-V%X0 z`FC_&?lSCBGVmw2%34VJBS@`|^applWc?x4)>rXN8WkOwq>WV)&VU#S*cY4lokx^lbSRbn{F)QiL zq~c^NW)V;6PJIza=}tuX4UtXHZc_`!<M)}`%b1iWUH+$g=~FFBh0u1VVkPB&*pSB~+PuS2{>sbAim_iI?T5oa~h&f*urtcd1gy=LmZ#R^HIN4R!@p9@V{_4k} zwxOvM`=n&VP@PCZ*_sO`d~9{7b*NsFr#Pe-D_c#hw8l^@9ERE@CsX`B!k#_fQLe;O z;XnIar){iy5EHd8TIGa$etBEsznFQQx z3wpG$OB-%%p3)MuYpz@vhW~ZNeaad$30f^@TEi9_jBV-VBl(Rf6(TK+7QLcYq=`M6 zkb`JWG?}0pYhN420eLdnV$Gi>Yx6h^nq>I7u!;JsxW3d@Gs|}c_@E#(S3?N4r#J$ zRF-dE`-n-Pk{^o$L6b5_L;u3g3Ly ze9m3Os^jxev2}YQjjbQo#!Uw+0%jLRBd=^$(A?*JIjya3@7ITF4q(C$5=y3Fe6az& zmVy@W)TLCuMXEe{^a<{m)X<=a*xfxU&WSK>p>>2I!_4cL*EXYzhj8<@%YpOn9g3oe{TdT$T#lJP#HX@_c z6K_30ia1Xzmd}e*d6|^fMR{Em*90f%^6v?H%n-#EDIL?ZG982ldd-vNFe6`fe;r?1 zKLcO%((Tbj1|IVouUPx!hF0%+et_>FQOrJdeccJsnJC^D%Wq<{A8uCSvvy>ooR!}a zqVf>FwFXa1PlJq!lFFBao*Aq@r1-e8YJBjYiMc$|UQKS)}uIAe0B_VCMjOdhiPe#JI{?B_GjhKi&JlrcSanAXPP5WHhtFb?IaT{p()cUqq(!l!i!?X^O;NAUQ)Cc zhLfF>+f4f9T^DC|B)I_ySt2~eZ^vjV`D>P9@I_t1!lx}PTJA<&wYbYjzTQ&o*1SbT z8w*v9*-(+CrI|b23XFVMJG!tD>2t(sy0J>cdgggM@NtA6*ta@J3_lu|R$RR|=1vmd z_bKG*n|Vw@qxz$Dy=mm*fGTxJs&Rpz;(!S`$hNjGOpf`^2f2f_3yXV}a9FAT{k`2y zE%SDCUo2cH27qMaCNP@56_^mWR!E3kU){m!I5Gyw&RI6H>fR37RBnX_h4apnNjd#! z0LG1B_pL}8%#&Wd1;E;YrGovwB*_ymChEMzNm!GE2``a=xvsEFxKm)5!ejp<7qO(Z zqc@II5viUqsc32vy#=VuDDDeLVapF^&!E9$c@L9^CKs^f`4-KvP^C0^9vIqbr&a|!rB>$VJzYw)^w4b;5;q366bvf>g z6ST*J*F2JEcLLGOYMKnZv6g?D6rzeApDqY8p?EHjs#g=0KA`RLa1fzAE~%@!;a#H- zNceb_x*@4ziCAEJ>3(C^>28FGvoJ)Rl1#=Xr+uL>?+Z|2$y{^HSr}R1*lH)s?^zER zEJUrQ)q?hnqtHT3RX%w8~aJrbGZ`{3l< zYd(IU<8wj%^BgH8SNle_eJqR<;M+y3|9W7JF;6ckSt@z_cL@(CZC zyE6IM!=1wR+TXXp`;;)DHqc}8 zhK7b^bJR~r_|^k3JR2h%&pLUrq@0k4a+M>+WZS_kqB5G)0UOdxHu@|L|RpEh~Pn>l-s;K5>8<*we3sTJIAVV-~X~Il=dK{%5DGkxNkXY z?WO~-?QqR0y=>EazG#wWcpG}0P*AqV#l;nZE>@Is-MQ45BB34>kp``C2N^z><|TP8y*v+%`|+7h#V zpQSvik8ev{@sgDqaeymc;54(@{6oriEO4+-uZDv=Hb+2X9#6LZI zriMWu2c|3FwqQDXCPHe)S@r6K* zAwz@k*ML*G1bD3lo6N-=K$V#l^+{)Jp>Kir-P4y*uHRfRY`cmlTR2d1Z><}={ zJPR6KdSl2jxowG0!0K2~-@9|*RsqoDI{*d!enIBhz>!+r{Ts&LoY@bG^-W-v&OWx- zQYO6uokY1Mx$7nb4FAymcW=&oOq$6YfmlQX!=={MRR;!{66!T7qfV`m@||L*r7_Bc zm+P0@nH(~lIA&#ByB8k~?C!QJh%vRmjVOVE7B7B%bBZ8LeCzQwhy#pEt%5ge7H$D4 z-8f)(Lzlxx)zpkq8FVD1I+=`=j8a-dqx%O|&<7{l7q%Ae#*tB|iA$g4nzbqJUg3eA ziJE7gn%YXuA$@K-NwU*|Akl#4KQ|i$+0_%i`oVtr)Mw1G&!ru5$M0irRgLh>WrQBmVhTw=jV4At}2Xgfq}f_36$Q3PnMk{-t{d*6~{ zdAh#+aPJwxTD1t-jhiw2L{Hn^nqzuCrRQtMs=_iiJ%>!Lap5K;Mj&SZ@;BCK-+MJ& zSAQuMW7#rrQ_8p9@Iu2?Yw^(swIb$enUnRImRz0JMin3N zCa@R}Z%T}X*Tsg0n&gjGJs?1(Nq7~q*N&eV#c0brS&$#FDn1a2U->tN|0{=0u#l{K z@ZZRrzwBmUwI>4Pj(!g&7kA`L7aZm^Dm;i{g+g^+&HS}t_dAfsmBx@pNl8Ikl$?P~ zCH}Wt2ATTFY;XcMk9PV=+4b)<(Ga*lY`T?e0rw&1`4BI#$K<~Ji$nP^9I-ffgJZ~x zhBu9OU8XNy5r2BT)2!mPfOq`hemC>S=Y@6pfmK^J14}M_04H9HsJyEs7r;e=CY=Z#R>N4ZsrqV|=4}`!__O+PpEN6ttP9e?O zA+*rTl04M^UA+MHvlF-XW!lkI;XyR(=K@sY1SDhOX1igKHPoTGz9&b*E!;@fvxNM7 zwlr*y7V#?xlSfy;!X=H2&4OyS_?_d?%ixQ-ZKQ2JgF^a0vTTd~(#+-)YVMC?fK_TU zWNTH2?~b<+0Nq${S+{y;@&jZ0`&to4lAP~)&0gAbUsN!z;)yO>!_LBq>9CT6xQmzQ zVV<}NwVlpEMB_&?e5aLIgoAkLT}YPn)UX=qp((lLB4RI995R?ZdhbVbS#MRE^lkFG z!=6E2+m=eiS`jB;H`s96$oOVsA`XeC>c%0tGVKOCCag)wJ5S3;;sNz3)bq-MF^VM3 z@i%QSv+Cojq_shQMY*})6Km<@&7*<7QD4*(ITfm`b(+I@x%rmU^T?Z>r)J^-R%uMD zd(#jl3IqM>e=vCF%b?Xhv|5buzb%VSBVeL-79dloAoySed|b1Z`@1al?r|zj>Ul*9 zN)x$yi$Z@(EfOdqZ(fF5Y_U*zKI=eVu~>q9brZ#yMZ+D9iFqJO4<$@g*wAwmH5qvY z-Xa^30Q~s5D>ut?%!_XwKylf+l`!|k?y35#G4NU(GU#Kb1to~Ime!h+cH3!_ZrE8q z3fQHA^u7w{y;MwDZAHh-n#3$P-vrDcEGibw6h+b|5Ih+4?t{4625v5n{Y68A(A{T? zFRsYBpFNj&H=&Lxq8Sq}kEwkeF1I2!s!h?;C0Pf{dEHUzy1>qiP-OT0*2@Xh2PwBW z&xQ&TFc-U`2pek{S36?MN0a%cu+W#S@USZ)mP~Mm2iBwlBiGogXE4VsCgB<-Zr3g0lG^MbM0$Q5B29CFI=tp4*)6({KLoD)tWQ1`7hJcZTQ);ei{8Uj> z%K*_h_uQ_H)YZJs9klhZT?xD{5^zsmt0LztCC5sC0zNRnzi2{8(Q@R^X}h1u+lz`djUx79$P%Qu3oN#s! zTTD>hNj&rYvNQ=P^R2lN#~$gtiLXlPiKmsl z^>+0e`SwLZqBgrtD4mm%Yo9RC&pOuAW{_>Ba>uhPNP~p%qoq}L1mu{+C%DEx2v}kf z8Ix2A)o8$eoAMnl!iD;t{pXJR2eq^2M-o5dc|dDeBLniGPZP_!`)&FxX{julBG?5R zBRD7cOq)hB!7*?-yIuztcm0bi%4)-^Xk}Bn>Q!7GktIPK8^qoWG75ZqNn9yj3S|_7 zOw$J*2k@!4OmEQr7g7w&cc&)a`ouCgkdkV+zq=3o&T?A|n_tQ3IX0?A^s84n4W7Am zCDzdN-Q@!#I}&p5%1ZKqK?~d=63!-dfyp!YQ&hp@??3M=l%W2DjQV{F_&laaTDcy% zTtAH#aC41?c_1Y1#MD80gG?sRHu$eHYogWT<n7=?7I3& zMnsCTfF-TzhHN&kgHUy885TWvS5OC)${P}H@!sc`N!J(s%s!~ zguL}BVPh1i@T32r$^K$u0NOZUG}4`=+|sDIa)hw9AhYV>#5RuCNa6h_GX3}UYfs#! z+HCq~uD#d$AM5Tn0S7-(|I?l8aYW!X{f*ne$=BEZ`Wc0^g6?6ZcBJVLl2eu@Dq4Au z|3%;5yD+P9i~x_jBc~h*{}-!2>u~&!&y+~SefQ&kyVb|})l($m$AvyrP7WV3FId?xZBaiY24CeZaG50u`GWW zs%mCuzvoydiF{_EQxuID=;W2CoLd~=(h0kwGdgG_Tdx27`RK)h;DPDwJEq`>o?z{M zW_LWCX(}G;8p$^Ipg3al!{`b~+|!~E-I_W0g#wbSi{FcR=M6RylbU8!cLa+(GoxDw<3sXVp zs3}X<9Q@#E2s&?CR7h+5_WNKJx`NQ|qhp=m6(M>3ErfgFgI9#njd6>)+dhX&M&98y4k93D{vPm_ zauqOtPqC5mU6x=C4gZpPqj~=Om$VYW#rKAuufvhyX%*?09#wk1?aepEOplBoCm?C$ zDYYouf$U3HuP(!`Ub>_>kYiBJZ_(S^n+cll5+mc>ExgC}Wf270dFJ@Js}hlq;sn$i zLzqxP7EKjx9c9F$NP{m5Z~F&gzfD4RF85^V4A0DU24|?djx^9s_qIAMOb=Hw=$X{H z1@>(G)ZKPG zJ6hx2_t@lysA$X~8Y0e|t8XCg(n30X$-6U(Hp6=$jMQp3(%*M*1kQKHStBa*sf&^)6{PrE<6yFd;XO-rpj#Z{6Ifa!)>!a^}x<}m=7w5bDwRhiD^ z6KYI9%O4pjGOPAz9rX5lS*tadx401B3a@m*-4+(sHd|>bwZp!^cO~mqd)N=vPP~_6 zqCzs!N;-Y`^);GIb3zy!tor#p>%)`ObkJvU0*dy-&trXFFOAi?O@BO5lM96*gcYs3 zUbJUr4GZ1d(ZWq!W~}ntpH^?*niXBnCn15WnkHpQ8SmfgY!Se!U@!-hxKUYp9Zqp< zSh{v!z}3LpT;TB}%i##S{utp#&EW{mtSepor)8VS0&Hu=_k+F?GN{<2nzzesio5Mu z4u(lJNgbYtvk&lL;w1qdEg;=p8;;mAdFmTZ$R?YV_L{`#<(m$zkh+3nffMY$ks!C9 zoiB!)ZH-b$DIRux^yu{V+5}XCYCbxcUQ|o`Cq2MyvKRHKWSq*wu#^no1c$?@>h6KH z+A#Xs{$WnYE!S{m@+jpKwAhwuXoh~`pBHx=|1@P;yFId4S^6_OW;{FB1s6OmtCZ*z zA;dP3of1CeF1I`Rc!SOLz8OEa>8^BntUucbMqENyTRZ;)F}~D=TK;ejv*t3bKrh3q zt;L+uR(Q`va4KhKCw#no{U1Jjzf>qSH=%`Cmte|eMkaO76t$hnYy0ywnUJ}n186pn?t&TV1_k{byc+ zx6%z2UQ16wkZGBP-(0!-LcTeIJ+Agx#W5(zd}myuqq`R;2+f$Ee_`8``Zmcmz@|U* zW0Ko?>YXK5e3up`MK;S}tSY}spU)`uRCU{Xy5U*h>4WbeePVCoo|C)&5}57{R%Uhb znu8zodY=x&K(M!%WPE=O-Jy<*@fFyFaYX~JcE{EV9>u5>CCUU@FNk;TtYxV+dVy)ZBj2?p z^4zqnRfboDN=zz-e^=^;(n~PKKPVywjvo#wZv>vER6Ls==vzNCS{dSE{-(;kcE9Eb zXs++IZxQ4Z1xCu}u84giK=X{<6WG5UCBO@)NyL=U(6ij_qx{IeYEQ??u6(t|+nf5^ z{VCKGh#B}C!aCEi@Y!8ZQ8O@-m#3;FwDA@_K))O+A~XfaDF*m3_GjywE|8}xkf$^ zUJeyH@XwG(H>65a_C}3AesorC6ZUM!Z>_TOVTWR}E&~O*i&@8hy@IZ5&2MC9?>LU1A>Y zK$zI6^@s#~K$-RM^TR`0mo#y?Fh*|oTVNtssu%_{3YwfEp_yL&3x0jO9|Itp4qoyA z_QQ3we}46S?7_-2$H6hS!+T7c-;aUMXdXOZm3)WXtH%EAS{eef{r~yYVtWnUmnDL~ z&B1}Vcrj{0&6JuI;{GnY;c5^WfBb(!yjTj$Xi9r1=+1Zo&h?s2a+RfA+R5Yc>>pMAV@-X&i=P@FVF#jH{k8uu8&`QY|!kUj_F3_7|GCOCo7~eH3$B zOzbcGrjXGz61%Xwzpq0g;KtsJsK}yT=UbH4>LD217cU$;ZmW%qR_rfe*DAIGXWd&R zs)f=Rrc6)2A1>jzBKm0T^shoksLa8NqrdiI0Tiu*ktNcz$|c=wSBe&K zx5cI?Su{I*wA@?X^krXM9t!EN_PjHYgCFy0Itb9J(YrWJ+dax0w&3;h_E!`%qracN zkD6XO?D0g&a<}iquE*9uy^(um8Y{`NeK>6OP|O6};zz1#0%Aggj&5hvpNPxOF?*8-#HP{~MCYxW{5%IAW&HO{ zNlT8s`zuo2TuApc^V%%2(h2Y@e{N_`qPU)7s&>9iWP>x_z_$k$DnHDfIVA1i$Z@&ABF52Jk5akGj@9hj2 zQ!$TIvQR0huaBMi7bF|A9?$OJ&dO2@JpNIl&iI*G#&v;v4qO8F%2rtf?zN$v=c(G^ zQ$G z&d%1a%1ubaY-O5&Q#8KVtueQ^9b#L^?YZxD=wd1juX20o#o?7Ri-PI!U!H06Txh7x z>qrWsnCbENR9wl+2N(Fw%Ra=OIIaVhFVWGTj+4-F0c0Od3Yo+$D{CeoZa1hVz;yfZ z_p+DRe6u2VIZ|2Y70YoD6d=fPyawUdZ`>Fda4@a*2=iDSRRJ+rhW{wj#8U)=FV9tn ztcWmobE|h1M+t7H*6LKZhOCFrQ85Cva@Vv|7$=W0@Th&hrEXQ7*Ny~OhVr0XH)di@ zn?Z(}fTV2Yna)ie;pR3F5%0Q@X|Wb&vCah;2Rn>+)ObH@`V>H{x#Cm6hSy*}+`g&4 zpJIh9UT*|~3L$+Bts*bF$PY@~NVcbqieHJnb3`3>o2>esu<6`Wzf>d^dNE4p_I9rx z=w)iq%elr2d2Kuf`O7=Lc*S;5O_zIUP#_gm7{#Tj9ek0qf8b({ux5Jth3&Nhbpq;{ z9_YMRGG^3g;O())Z{WVL`XGAQjW<;cP6`iOgm|D7CfMEr`-|rA;{0t|$Z{?Y=>Bq3 z(((If<-XYx;q;JAFvQRH%&`tT?&bxqTZ&nY8UJ3Tc0}{LgrLH^euKWoHFqNib4L{E zOgzslOlVf`DE6fI1-)2%bwl_IOZjPNXlh7Y!9B_v1DKTSXEyIEDEtX`497d48E7`E zQ7Ul|p0*(-@*%!TIbmfOW8^$V-E*Hd%T6@6E!3$UU9b8%ODEX4^S05?8c#V`P`row;sN)iXN^D{a+x(U?%Su0NWEIV=E(v>0v&6}`HYqWO1R0) z+RmTpw3~>gv6+r$E_v>K;TTZ&>Zh2!NDOeGrEG(t)R%ZK34BnM&}0J9)$xC$E9*J@ zI^O}v9Ls|HtmU|hEVxk$KR+q@qB|g*9U|a!W(z;JGji8{I2!CgmU)022c@w0KYzX#Fuy6~8J=manOfz((w=)E?ppq<22rPo6M4-68bRk3o5ShAQn}>G zE3xQnGBU+iB`j8kFT1&nuFC0R35Kgx!}kz-sdPa|92d zw!Ii-({nw@({7bM_qmLm1Edqt^IoIDA+mT4l>|{*QYhiLYpvm!7EtL}Z8d>}C;d8|}m1N;ySG6P36#jgq68x%36R!>w?&<#G#cr)85>P2Tl zhw$W83aM_Wm{`fCi70B{j`9RxTbJ_RZ zSD&HO^LO19L`j=4<@0%wO9p%^YCX#Om1Q%G`!VfR6>N^fB`RbytefnhRz#Fe^KS;J z<@|UXJo*CMT;FUY4lMGh4#5vus6WRbVldAHm4Da`OI5+OVNNNAl@+`qdm`w0(Z3N_lZ7F(&G)ytDo zsT-$Ih|YI?cH}C}Wn*D#n(JcxCVSRK)(p}eWj)xeec=(*Q0dYL1OJ}vKmjChFh8tS zJEO^P=bs2nJ@&9=16(gzx>-V7>-7^_V^w|JH)rGdW{9O=b`Qa%uQgit(oT!myxmo+ zy58=^GF}GmZO@0A*#$jgj1JMZFIxd4lD6J*Z|l^p1B=l=DY_KF99Uhfk@NB|?+rji5;a5(SijNSNhoUH(NFXz(_%Z6d)|1b-K#Fbp+~-4pW^A-HsF*Bevo!N3 zJZsCV;vUGM_d5`U|LFVKE^7NLj?|X?3t!t+W^X;efP#OvY(KAMJnSyza&w6DBKcSh z=YycQFldYW^t3>$U`nZ-rfXh%j@RaJ+bI`_NYq4K;Y5~|6h{({)=B#g*YE|i( zh2YgWB$yR^|Ao-L%q8DrF!4)9VUO1raK>t#7hGDNU>AHKdT0H;DH_C!e!Ei_jjTMb zayiusTQ>Dh)vGuxg=R*q+!+~PE_Hl|vcoQYO*f5M6NeuxMQC@2Sz66o@|7Jn!;IGk zW|`M6xQv-NcB0s1U(dqi7}pA()*BV&Y9!kbysfe{A5~qKiyC~7NtZ0j)z8Z+aa-Vc z^ss`j-5bf*;IT`xXFxX7f8}ci`^qpgr=~Z(s_hZsXn6W=DA2e9%UdKFTOFH-C+#h5 z6Piv1#TAcdw(eCO1B;w%K;_jIG*LItIOcU=;30MPy5WSq`-t!@etvadZoUUkpFVY9 zuK|g@V%kL(afj~juV}K|7J7ASyqvFyScT?vyz~+2LP9hnWOpuuq}U_=q6enu8F;`qL3 z?rLyfr@U^54+MyxJ^ao{DfY{i%vJPyB_RqH%_QR7f|D$<#`lM_8xO!Q*IqXd&clSRbhr)*4nv z%yxypg2HPP#FZNp=Mk;w%$N9S48J8|VHds#&}$=Ivj=m16Q!WTG$WiHCs3 zV_2a!&5tL?98p-EDWc$N@Y53$v!~9> zEgsUW{FcM{p;I%PVaRXa=pC@n^)T2X)wte9{OI+eB49B`2iHlL9K?3zmKAY z#9~iVT|1=y4}%c(@Zv4!>@Tl=xgXRfNE$T>XiRmS zsLL1rFQ9yPAqevF*PXrfdfI`3GN&BU=X*E93uLWG*dVGqe8737i`^rR^nUyN@F6fD z^Nfe$jCxSCTKF#GWRCmqhs!a1o9@U;1Xs#s6_keAV*}OQGmCFdNzdmL_%p#FXt0}i zU;S8s35bmN1k1?qtsdnfPc@K0b6*=D+cut^k|gg56+Lgg$f3AB>k@RHbspqYod%YE z_0)z)uc4mSzjK>yp(7>WS&oLHW`=%@EHuyTnR%Ipw~ASLNilK7uk>k9Zit0tj7g)q zS6QRDua6!m)#KWOOKyHn?$};>C+&Xzjx-ZjCLyZ;Ovz7RJEr^;puS<>DSkO2ubzJk z7U#}TJR|XHsc_jjZdb&*#4l%Gn|WobV+sqx9Tw#W?>0Y|o$3eatYPn@b%12YGgrw-P`q!~1uiv!F1 zmNw9%?GGFK>SU5b=sR|@!nX~Y$%4k+3DYw>ASTNV@GPw)=Yh>f!`6kt*`(l;e>0k}W2PSuV*WX>2L}$M&|+ZVl!Y zP4E@bBEjNmL3odY%q&K8>yUAls#_Hix4;rG>rAQ?ZF^Ic|3^#?nS_CRj3v!D9PHVB z3>uT6?9RCOheHk)G{LH0ZOGDm&7oj$rdi1gVnzQtd%MGY-)h~X2?n2Hdi@&Z=lw^d zW#fRhnCA&Xm1S44m?>P2L05H~CURKe$fj{GNQQ6=akM?|yDC?Ql4x9Z99t}hy@j`IMOgXPsMK3%jidxDDXx4dC$OC+T23pi+NtH(_TgbO^ejw;PXU_y>PT3W| zD*p1z4-79+>P7dH4C9-Qj9Y8yLD3F*>!ray?KatSe%gh$_T@Y|rrxO|PPBhUnWON= zA@ly7o|AxcUeaeLciH=MJ#-6f74;te_B8M*@7koprp&wX&@7!&#Ma7f!^bS1 z1@-NFa(QjJUOPra=L4nRo82}R?@gYz+j%=0Of}0{!AaR#=yNf$4~DPR5@(kd!^FoU zM-KKU0FM}Fu(vwJs2$C$-(36}3<>ZYCNuZy1lye$UsUs*`rP~&GJBYL@>k{Wh4USf zn)-aChjQVgYisUH=%Gr9AE1?Olfj;{c+zGmo33Mx5Jt2bt>FRHX@dFQcwO!NS1S9% z!sDMf>%vITasmA^{mqv|9;+Fwdqs$JgeP_iq;;)=oz1DGy^?AvcGRI#$3fX(FKNCo z2X8l2jG@emymZ+YpE`2*w6 z0dMeOBM_@13t|h7`|nx`D-W6bys(2xZi<+CqS)8>?@w0wl7t^9E1#ir9d!o#;Pb|$*xN-m_Q~pCS1q;I zC#5+QvaBV4eWZMXjb`pbT^WzwEjn8E$@Q1}O+&=P(!ZefFn`I=HXatB=GSD{UsF=L zoLC)J@cM8+ful2m26yxe7>b3>txX+?m#LiK)PDdEt{XS*Yh*Nz5hTf+iNDEu$W2gp zW6x}wcVdFFHDg5Fn0WO=o5EP|JZDax{A^(E{lMeCfJs?yjnI>;*B_@To!e-%{kT*} z|N8YVKB{DBqj4(JthxiSg;MWp8g^}mbtaIdu?~8&7g_V%8}VA_4_}t?u+%rLQ)_Td z-uOnt)(63^FKK|8uDl*y-U-d{E?dH<><3$QbiZj^RxPJL(xSTlJV7Ry9g~U?9}O+I z$A$;q?B1Wj@&fHMfO}W6v~of1&J|hmdOZQ^|FWTv$ueig=*P%8HW|agnLTpO5gwTh zE_8F|zAn2@ppjXr<%Gl>oU+Uq?IP#u|0;U3c~;7*vA64z-}9K9<@Fz52~0ooELnv<|1-RbYQ^6$AY!GnJQqP{x=c+Ea4bPfgLygzG*kgc|CBktAG|^dZvLhYB#-AFL$g zdxQ{=xkDriG5bfhE5C1oFTi&F!2$ff0Q|Ka@S76&e>k1=zj^m=KLbOKFjau$&`0={ zB#f4>p!i-!-B}Wmt_8CGF7#}wdF^|!7e*B+r&#U)G!*I~bgqccwZ;px?^kK&!1OH- zpHl&RyXbp(7wMrGpha(npN534m#4fE-zCxMWh-7{QN2WeU-t`j&+f*KrN%muiw8Px zM)4a%>B)jufyXG^@Kl{1Gu3z2Kde;q#a1tMaLpyi9ZbLcw76;Z6TpeX9a4lp41&n z53nbpKP#&2Wi%X0w9&(4pFJD`k&SO2q{hF3dQDp*)kwLcCs=pL?UZOVgWpeTc;F^f z{%IRBDY&<<6lc|0f}=jR+@AZZG$hl@`TD`FI<0?9O>*nhPXDhTBk2>A^{$qd&v#aN zUfll2t!E+Ce=tUq=k)XD^ey#u^E=={h=?A-Z)W&L_GcD$lNh7CzCs=vs)|3#l~>~H zUZlv#j^;%}n5L!vx8=mQ8}3N`?cBD*$vEL3n~=AEeQ~hx?Wm=y5T~lIrRldvoMRE| z=BI_M{I(94G@-y_jd*FGJ*+NE@IJ-wD1o--m33Qz_Jc%jWPR~mgm_2lT2=>(+lIC8 z-(2OBso?0_F#}+drMd7l5+2KjvG#~OlXC4xk3LixKRYx$>M`QORzJj6n`GRX^)#pJ z?vr1J#$~p779<0%U7f!c$9mXyifpL^A!)p}6ii3sPyWLn+1)Jcn;ii83>8_GN{0D! z6@OHG{6jFZ2vwj*y5er!>Yfc4D1ps5ToL!-bSJwU)q6>1s~y3q8egek1f^UW(r@w- zw8xWX@UKsAOsG`TB2z`B!@FOXR@*$C_rD@$b4t&y=VRUFI|>rh_dq)R^ep?K0l_1H z6@oF>u#OVw{z8AMv9}lVui{DrA**j=mVoXLt%V0joZB6pJo-(dzF65xm7J{|G_#dhES3%s22rfgYaauutveB*|dG1$!(^f!Ie zV&L6M83B!(_g)n9;;Y@(avUxOoHC&IDS+2C;E%z9VSe)T$WvaxB;sp#7d<}9W?yg~ zp61`aW!D0*)BbG|8aVhG;;wO}B}C6=*gPDF^S2kSo8ZfiWAW30z9a9SoMpd53Pl&h zsqbnoepDWX7yVvzR8*1G8%h*fF6bABz~*ji;J|#-u7nJ1fR!@1VFkjr7dej-=@4>$ zd)=V(U;dU)f2mjGEpB+gYJM1E^6T^vbj}IWGNP6E_kB0F36wp9)UoO@tP$OB>2#O^ zc9Lf*)21ehmkP`Aiw<#e5o1gqs@E!kP21X8+7|O*k5^iiJ4y%CB#dG~Ua=qzYd6$8 z;dQxOxOmp8D`n_jJCkR67JYWlYe~oMkN0_J_f%$hZ=3x7p8%ssFJ8dTgFUrPeWYiQ z^~5!RNDWf<8=ISHk79VVUcTgOY-o7=_B6u_^6o~Nr4)ri(XI3K7Cv1b1=BcY)||MK z?7*v`lf)IgdDA)NW>?fhN%c15#6clzo(b69rWhTqc!^25KjU0arj{|HzK;+rUAg8H zRz9AfDGNK=GgQpP&A%e`_8lAQ-8t)4VJ>ZlvU3HzI#zt3I67O_6{-m_Q(=`B&@;ea z`M-#tfxpG`YXy^eWMoCOMxNY~GQE|+Yx2aI|L^Xy(ZlYtd;gd2GHh@}1PXkMN(WRj zGofhz*pVxAD3y18m+&g-$c(}VV~Mfu4lC9h*r^mM-@sSSRkREnCGYWX8#WcDy*Y$s z_`!?Wrn$qGx*s!P?FQ%^Zt#^1&Re&t*tl-qeB6`zz`i}6 zO%`_2Xc1&!e%c#E2+?nW1k9sg{5Z9(tk{5WXz-32FSvKSF>8FuV(zI@^My%JE}`qa zxR+ltUES38TAD46r-p8P`aLWb(+W{Q!2i58aK06PL-<50C zj?O3U3C~U_A2Rpg`zMbyHRIYl6GWrhkDb2sjen%nF79QrW}SVN(o`A9Ox&8swZ){h zv^;b#WdRk;v}9?|(?@doO-KlkE=bTwmg0=lOp&pvaulgh1y(>mX7Tf)7D{!|`vN`I zNE>k~Lo#Id+LBx&C|ExU=B)BN75g6b_GJoV0Y*eTD$>a)oCF62zxj=3=h_dS$?$D8jv? zkqWEq5#P3KK}Q@C+kduVwK-rsHA=uyOa_8)QR=yua`TZuP9?G(4szLM;w<@4v;#Q& zRmFD5e~OYZp!QlB#yV}oQH^RG*{p$1>hGn<8t4Qb7r|NR{p!3kq6B4PhX;c?!_8y} ze89eXASXh5sK{br`hlxWm-=Vi?}Ywc9HcBaPU|@gr@U~I`(P?ME?g7#x0Fp}V?(lE zyf_WZ4JQ$Gn_k;9R)YoX>n?skOG|6fR;MdPme+^v&L{!Cw`EhgtlkcZAc-esjKsrP zgQ^KF{aK3q#+i1a4V*1PWDii89P9fuwh7ivo@9l$?^-QI3JrutnkvFgh(TwECXJhC z_PvvUz3sw$dlwOo9N5zi$YEY5$V39B=MVeMtSF!!(1IXI%BZ59%fO7}8 zbe37++?BhZL4BZoocds&=ousWPi65<)nVh-l+aK;}=6&81mwNw1szvkrE+m)nH~Fajb*DTv)7KTpFJk4VuaqQGmka*5_-ELsZfo2D zqrOHKGpbf><(mG!IGXF0d4?eR?8i;2J66vMo|T0;o4L6aUEMDz*k7;b?62MDG4OWa z`n%M4@Tb(c!S(M{0IwZEOBEvjnFU%~S|X!x&gH)6RqqxQ^XdqlF4vPSMFkYHd5+yov4a?q083SVlm1s38ktIGtk4?D z=WSe>zmXF`@c@)i{p*=X&_Zui(t1Rr`GE~Y6nwu0{D%OC-+@N}Yh4RKq{mnIFKWj4 z{g3ZP%<584i7?Q3Bba4t*r%v`l5<5 z!9URE9}fogNv|7#s`yIAhjSy^PV=#R`75f|07pI=?N=wcAB*-zAD{-)m61{n zptg5S`@^NT+&U3J_(_1i4>NBpGLh{_h|EsQxP3ZxNISnhu($XHI_LPVN}bO$~A{pKi6foMu*C71r-5T%rlrp;B%^B36vPesQU zqKl4w(@B!WGR)SC&#g)xb~3okbe*c482rvU`m92bp=vm}!J_Osu`2s0!QI?(dAY$F zgJ7tN^Jfrx_u|E=miGjAnUPvA+vt@Wo=1VQ(Rb?A^Q|T|B_;9)+4r6TeJb{PnbT`) zsrp&uBnAE_Pfgcq*9)R zsbO-m)>w=0{88<#Jk6vTulBYoTsU)W++tsx9EYmm>l_1b`cS6o+tIFzCERVw&pMN0 zs)5(n=+;tDg(>xN^aG69q?vS!O(=?eyy~qq9UZdd;m%T2ywezvcg&k+YhX#K6rk_j z7`o^A{s$!swf&uyb5W=Tk%j|}ol)QcGu-SLaZTG!aJx72E|xniDaixqN+$#dN!=oR zxe}tIk)F-1UJmE_2X48VUhxn5Rop&DocLe#tAQF%=XhaT-MOY?iHz`&K;|n|9(<^x z3dU<1cl73Z8Te(rfkeQ4AD=ll&6kR2NJi|A{^2*1=k#Os#n%6#!IO}rhDNGQt$m+G zv?77)LHr$n4ee&jaMm#>SEJkRe0yAR_=J~rUEVUOyap6n48l>TH2FIdZ!pU}Z3AZj z+<>l@X@>FEF9Q(8?m(?p&0U^JcE}jJHWTP-wg{6{g@(b(mzeJ=GFe;*N!L*vtY{(n zyLwxE8XDe$5bczW$S+=I+_zg}xZ$T%@S`u3dy9F9 zMijRCyoUkdG%X>F*RW=FM3`r#Y8Sg}$O{#Bf5YtSa>MlTSj6OPMTKI4?Y_ulZRnM& z-BsR=i?gfC?o!#A#(+3_BA&SM4;C`4Ch9zNt@#O*XzLii)1Ss++i7c%1Vv}f_gO`~ zG540>$GcIbhFR{_J_?G52YbGAhafdLXAuaEZ?vB;Q~7S&05OoI^P{ifq5rkWGxHcT zMB?zew9mlwE1k~M(d86GuS@!Z|eE@z}gbqi7&BM)!=;tpx0s>s20F<3xAMT`9KH2h~vp?i53_LQ2%)G z)!>1a;;CSmb#2D}aGTmg7fB{WA+nv1IQ9iTzRe)i)4IE5hfqyf`l3wOi~-*8rQ#*3 zm~UTZdY;lQq$1uAGw#h$+dAFw9j~YIw!@fv{PpQ9t8n-FWCBoC^w(cPozvvm&?>mv z8`4rbd&8R&WK`=k@xX7dkLFbDpjue3s?tmTzO9h{jb&YeujoF>_X+hL#V(6-BLA~! zZmQI82$6e3nYQPPT+3N8S)VbCx3`_r>=)b0fc9E!(SoxUr0&qCDcEAan3I7+q?u0H zz6l9zZVP<=yn;E;1Rz||nXhP>ua}cRGI77wKsX98zk--&5iJ|-q>{osP*RYfBCF4G z-`nYOw8MZNTyqxZs+dL8vsGNLboq9)Y9S#%PutNSKyrqpu@1mUxKtoyyAR+?%i)hQ z1>2q{x#+<3wPK2=@j%r8BlxpwP$4w2=mCH?06wE=wSj%g*lieu zNGSMtmQ}6`y9QI;*w_FjDAQhw2fJQ%o_O)TM(Ou1$=15wsXP z&q^qykfNEf!z4;7uM#hl$J4jCa7H?n?L}pPlgj&bz2S*TlWawqqH-_t%4`b&lnb#2FF~R|qZ`%G%1{;ay^ES|93J;`h|G^#m4(RrP;P!wcwf=Uw6&86A z(G6=7M8`&eZQQsBp)B7`cm?Jt{7w^F94^jUW``qrlw$Ui-LNgaes%9t7ne4)@jz!# zw*Xv%6*6EyR^eE{Phfij^c!@ig-c!Iz*!G}%*f+-(!cTkW!OL!0d?XKC6H|mP_b&4~Wio(1Yr#CJ}<4l9a zy82G`&%iNEqUjW~AH?_7`=%SP|9L!kD<=&?tpZ?^fU^uC-(Kubb6pz9$=jzauPW@9 zF8A?12|0;>FYuEpOv&SB55t3vNPSI-!%Wp{~)@oqp z%2wV}9OBXcfi9+m*{me!^DL3_I3z^2>E3XfhWieiVstv4KnXGH_3JaadgZ(qc=fdl zh`ZC_oT8#SIV(&*S@OnES%a!Grp_^!0>8%Ce`r5UMV>j;D6c+RcA7L`qnzLVU})ne z^YB{;#~-oq^!&U$a5_?lhO~oTy?S@*!nLm5jcMC97aSg+1B@!OBlWJ7Z?Z5mt272( zNIQC(rVA+SzgSM%$4DP_ORc(lG88#4UzWZL0ceOho~XH2=9)AWrqoY2m3Jc}8WLo{ zsb(F0`JY&x@-F*;kpL5YiYOkt*xzf2Re3zoBD{}fzY*B~kd}I>)q_)%lx~ugr%t6V z0{Q4@K1)kWja&`RQ>RZmM|xI!g~Z_y6^_r2Q8M1iP>cayH@G*J&qUvp8`Ybs+%`7k z-`A(%1(@ajew{Oc)Xl7p66cy(`5AdnPpReHyX?xd%X)7Xn1%%~!p&kW zNp?BO=Af5$)gI7TD@7RnS0dA~|HbS6SBo3Zq_5jTm-NK>ZzgWYo)^IQyEHpFAi#F= zAVUiSO9_);>^ur(5A;6P(3G3;E&R!>BtR}8F%2|%2Xh2Mfo83$*7nqagS_rfc86yE zmZs@jE-g3jI_K0`X!W8|ZLUJ%Db+c18>A{Q*{$|ah|PG_gNfI%z+kFTFV*$0WK@C9 ztZXl`ArRZYZmu_*M4@E@BNpr-sI*FFv^gj0d8F;WDty9Id~a=JhQfZ$OapbadrFtMBAv zhW)Q_fJ7Q#+|T`%h6lWa0EFZv+Hs7@8UOMU?|{C@u8ryDj0H4_SR8VZ&wm0!^bHCR zcX!3wkCx?gW06_V8U6hthV9xB#~UPdo1QqSyHux=CMWyokt{9f98j|B+ph@6&3+57 zSDMJvf+_{m+>VRRxiP%YW+fX#0M?{G+V3D3MR@b`^FJTji6Yf01w3p1(H3Rj>7(sTgd2fj?Rq9|hp5ec|%R#_G{a5hMHgn?`zr!Cn z{xsS@=gXd{-eP!7ff2#zfQt`YA%-|CPIa9#Xe*RjLi)^^vyB1{^G~0o965RA{+W}h ze+Bu!ymIO90B~zk74J3C6WsS-Cpw$8C|p%kQS20z=bJ`D3kR%n^SrCP&D^4slF-{2 zNj(Fj2Z19QiRE16?U2|0Kg$i@E#^ezqpTBy_CPT;3N zv7|pF{O~drXQ>(DF@y4Zt`7Z!e=Vg1xNIYIC*NyMv%i%(_%cn`b%0Q_*-oweml z9`pT|T^w?}l`?L%NM&N(a|GsszJjqqqU? zSeqbZMdQ`IB9#|XY_@Gfv$y{`UT1VyIiT-?T$rc%*F2Z5n0XrY?$L&vWSE<~$ySNo zmPX-~s7hb2v_fOyk5F7*~hEG_jbl& z>7;!M^K5V*O+t~x4OuCAIe%; zv`wgut@Cg{e*sdHl~JdXK-Jtejq!R(d$g^&Bkh)FwK_K1#K0uN9+oWY=?pQcs*CZm zgXaijAe5>&yyX|?t0!onMMeUihx+~Op1gc}Z7edtHSQWYF*7bW`}VU(8ps@}XlF9A z)MuFDz#dqY)Ez9$=$3a3Z|ax)HYncM?M7ow6vnM* zFbbV8R%W`qdF83FiBx*C_e|OaR8#M!=XCZxbj{T5U_B+{L3YE%qa%im-cl;hGF?(a zPiLKe_7#S@>#L~Oib>mEpwU2AFGl-_RCLCqAL)q(q|?e_3C%Qxu>(?8-Hb6eNIC=1ZgH#1~` z(z)YCtD9owT#xly3)N9uZUge!+C1l%nH9WquK4JHs@|QBB?=+qnyAZ}n7^w; zQsf%#8$Gw3*WU+HAsBf()0)Z1x(_GZVD5iBwA|v;FJYs5 zJ&S;K63e>ERw8sFDR!u<9Y*V7RIsNU7l5Hs1B~HS7Pm}oHU#Uqg%?praAhQZOxIR&wng%g5X6=(+r>#-D z8EDM$h50T=`V99V$v3d$u0#pPdP#OpIfB60M3@^$+qlJ?-@FR*p1g2no|e(8GJhj@ z0e`H%;n))|a+JRtRS9{$l7d`*DQ^nn1KFJAU)3J+Qnj&tg}l+2*ecP@3L6?9 z%Ly?sy!uVhY5Baf6?K-E+bo+zC33unb~f}2)3SnsP0K`+uiR*;{_(%;xq2>YoDPwV+AOPLsxDW z>nGGwW_Zb(Adl67uAT2)lL3@36#=SeB?jX{aA1OkyXqWtSjwDld~$O55LpiG#7zEL zdSUkXP&7O0T|%H^?{vc*_BEe^W{pH+71JAV%GwY^(7A6y)3}J+%u7pM_{OcWdnEtS zP*A(;{xxmStM0i7&cr#pkONZDhL8H-QIJs+1an;=DQ|!hGWPLOSJFr$XJ)yhQ%UJ5 zP*z2n!LdQ-AMfxVhVPMi5L=-n_}+3lC>*x;ki0)H?nY|Hz%<30M{$k6XSH7EyZU*q zM6jc|Uw}JO%N>iR?Y&>PmXSQ}TxKQV-%7Kl%*D}8D+iTdH(f@}rDYEk;~STci>?qG zTVM4O+F}-Xgqdb&nU^wo&tR`eJZSJ8I81iQm4Co|fr#p;|I&qWd6+yRX=(;OTP#3j z<8e&M&YU6gM&m6WlbCrnx4g;msnh7MpiEbR8km38;aXU1*j|X?;&AE7!a>kz^^N>Q zuyn{n)XU-!-xYBV)p@88mt#2n1;fG#hc3ku({ApK&@-T|!pEHT)nCO|ZrZzp+uIK3 z?v0a!lKeG@Vr(oUK-{~t z50OmG2`iiFeZl}DyL5f)jf<-+{vEbDR<6m@S7IKy?I9x}+)WsO=P_!>SG>BU+GZX< zz;tDE%vdZp&OJ8^9HSeKKK88;SxubioU+D`__?5NABRwXN+><9UdGMTfO=5q;9L(* z*7;;){Z$<2T&w%wSXa3ESk~3Q$kaI4yrrZiIhyF3B|1itOSqw3)M>J?;mN{el@H$! zJkIGw2b1ar^Ig3QH{d*(X;NwGl6Z9Lz_jZWuG9(bUZ?dQX)iv<+KXBFW{DJ!X{SZp zhSr)0^QH2{Jjq`7^4u6lp{UJAkVE(|O*r(8>=4{4EbqMIBPwY;fg#Q>Zb04!dj?^2GS_0KsR0R>QA zw==#~cXRl}sI8z@hCSVVdARvqTAsVpQ)94D4qa(Id8q?TBOVFrfQ>7Xm4<%D%r~uO zMiq?I>+Hg{H>G=?m4z|x?~P%XZ{?zhDg7My4sy=n24Q=>AO8W;$G=s-D?9rW?aCe}Eg(mwYSR2*uSt&p3W^Jt*lFg# z{VknaiaEANexl`WX;P4YIQ;_%bG*aRL83?GzJujU5g6E{{r!HwS#C_oc)8HJ-8R83 z<#^{lx+S?3sh+~aDCn2;A1k=I_zXr}*YdiG1cUwE18VLbf4i29rd7}B%1WQDp2znW zV>>Z7y9?)!5(pl^mYrtTGbVgn%b6F$)oy!nU)NW;Nl+=vXpa#4WT9Of zR@p96dP@y-Pv%-tbuz7PxSg~sEYGgR{n?U_=$@~l-}M`%!RkuJ=w%X&(^Q#N+~!uQ z&vZM>o}}y-{D>l34VtB7`U{XPvEs$96FHTBs0>-URM6JnH`9QFA11G~a<_5nR8>^`?n1JSP^SqE$(olUa(JA{3zS3RoWV?zTS|FeeT_s)uIaRNn#%7^YA;SI!(yb}zLEJ< zO`P#;oocY;@f%H8&h^Y^CxhpM-t0BW7{1H$y1jPH5=k22|*#BSLb6@tz^Po;jD67EQ?Ms557w= zC)V5jrzBZu_x;-8+?R2O7V&JZde8J^{zqw&y-{jA8@f>GXUc=IdA=eXpVw0!lASg$ zkP%Fi0T9-reO}*DD-)^^0<`ZTK}!mGay%x_Pd)PyAyVO|A$ijGwLG#e(`THyXy#e^ zaa$(QeO>*YoOtn5QORD9eB)fG6dPyVA@4G|WC=JT8j>IQ?eqhlOzh<=0100Esb;{Q z|I7aWQilVlA$$adIA*j>>$vZjNNNK7Nqt62ZDdD`$aPy0sJIR8DlZOXymz1A1hN-e z$ZtI`Sa|>COtp8974AmLVWczzz4TR5q0OO1j*e4UGXb-&H_@|Sr6rAxWeLv>YqO1W z)LhInm9D`2I^s(_2e=h|lTOwOV;2q9`?2*}Yg$IN)OB851}2}K9wPaTsu@wGEiC@- zhLVfcwAP~Elq#ocbDZ-Wn;e0p05VR^SJsOdv4-pzdB&+Yx8q;FNKWI)s2G_UddYS~ zI106@vSmC=C^2)Ia^I7qeukwjzRe?G>A@7gEYp!7_K`V1abWm{AzwbLG7F|^7ALmn z`o(wO50im@w-Y5CD46T8mF~S#G3Ay-o$JKmc?%SdE!v6vF{m^&wI5fGS(ovWFp|>c z^ON9V5eYjr+p#2|&a9=@=#8rNfqyY8ihgUyy(>l zKGH25UOieYA!qr73Ajk33fv9TLqhrD&+pQ%n2JKI+`q($9u)4ILMfAA*!=zn}!hoTg+25GR->JjlDd- z8d@>j&clGzQpiFV2Rd#rcb9Tq;L`3AYFdsJ2y~ilSBUgN3&(kudxa!n5WwN{?fP(8 z(gEQM>|Aw@YcP%6do^TF-%2r@+jaRHwl#_`jSUgX`XrLHZ4h`Fy4 z){Xr_CqgDY0~2EdEgnLP+nS5ITQzrt6owO{GsApBGedm(V^-~hI+)PR`ElLKAzwg= z>qQv_OfCPZCV80ovyky!#W}6KN1Rl{ineJMLPUuNe!U?iKa2$Y@Epkx-%k>gqSlm2 ze;}~7p9APmKPhQYk{!$Q!EZ#+%CIo`Y+JGrSEHc43VQDA<>+&L+1mFLwZP3~!^OyY z5ybiVZW9%ogxW7!Yaa?H8lM=8<;OFgpY?bQ!IwO!GrwWn;BoSECY?wOrA=a8&mb>E zyYtZ48ExI`-8L2Opm+u9fZD4=<&Ts9_3GPi{F1H;5G6W{MDR@uF!)8}>}Bs9+w=Kz zV1tp868r8eZ{~6~6IGzRN!CC%LE6~GupnQnqeflwRT2h{|8%&-q2YvLoy0s>f9|W8 zzkHMp2Z^^b?nXQF`xw>C+yn=LeI6W2L*B&nX$N{RPV{4tw2-+2xBJx^OO{8(3;ZD@EFnnCak1KA=U zM_E(hdcQcgFy{2t%*e?dhC1`z&k^v4GhpP!aa;P?4m`zdRTgyz{YCp3^Q!vMuf&M^ z&W&}4YV}l062(iW1<%3E9VIA;t4P@3Fz5oZHrTFgt!enM<=E>*L9Z@>64wa}MPKm< ztk=HfHUv`N9XJwTRWKuXI)lfz6lpXd6Kc#KWA=@?r!T<*yo5?JUM#?0vjd>h98{h_ zi=o3naNY8faS$_XqNs~}06CDnEQ9Mz=9$9^5S*D`fTV#O&Z5QKql36$rx#-K9z&Hp z6^>4?Gg3}<<7tkUBeFnBsx3RfS@?Vm3$4gF8oPMS3`nY|cHD^D5E7W?X5F$j-w~XO zl&|3qi$Qf_ndPS=e_QkK>H6w;$V5L>1l=1-?5qCRTgEc*a|^(0?M62setgQwlLmYJ z;{<%8i-rIY@F!vH`z!d~@)#y+s>EIjkqZCTHZYQ=! z=jWBqmg9XneY$AS`xLE2KE);ozqD`1DFJtZuJnIISIHnBqdtZ!g(7Z!Jgrq?HG=rg1v z9pj%Bv_(p`%7?h3;ODt6${!w4E}O|ROKjgwbX@D+%n=ch#(j=S7MF6I?g({0)?0D8 zKV%%;Ozsyg3}>>-wMVasB@XV_VNXPp02Aa({QWSJ+4azZOpJvO?g~gu*_-3&z8hWe z2bY}dZ4x|COWpE|pV)g_Pc1??diIT$*RKNI2UU{p~ z+>yfR`NE?$#0jI@wPh?TLdM6Vd8t8XJ#xXlC9C(}WI+$#s5e3k3hd0e;0vH(B6T0_ ziBlPU>lxCA66-zW-hOsOXO@t1A1yk(Ln) za_}ie^^w}>9TDQsxz_g<&$mOO_80M^)|j$zX8%VdcOhaomJ$vo#I9B?@d9&Z^A`$X z1gFrPT+Mv?3)g^-S_{$7ZH2ID`->7p8=h%$X5t~o3IJ6#O;t+g zCR{?`%X#OB82*FB+*{Plp8F*{z}iA`)G{nS`yXxE0PeKQ1_rAX@C^Wim64y1v!nwI z+63|nJ;~^#N5tuHa5liVi4u3WYkkW_3ye9TD~cDg?pdF#x2Ms3?9L0?H0X4f+%yRH z8Dv%CHGi{qpVQe)lD6tXPY3ob7i`Z@gvr*#ZBy<0WN~1~VgGL>n8yboxY(@YE-0!5AF+HPf2rq%uXjt`Nm7(x71mqS>-AB z<`V1|2-r1R#gmHz`COfCQ_kbwYqiF}rHcMX8x@~{iMZbECDpn%Oo4pe(wj9e+16R5 zDxbK8HV}2&t#ZR$Pq6!H)tY;8AG-RD;?1$p+W$ppRW3;2!WIZEz>|g;ruSy4uF;PI zw+>!Rf~})(dGxo7_eXvdAbM`jynFuqh@{?2_;B`pR#HlT7#Nk2t5uks(+()#U0|L~ zIxvYyogCil{$|Z-X5J}aSC}rD&SBv-^9#EdLp527gVA=?`AK)Qvacud8tgT`9DX+# zcrfKXRqtK=uktfg>6i0-4_urkXxwmuHTpa&kxsxY-N;Wt(6I^_VPYJzRSYnoL1u@C zkPF>u#sNgmB(s+Hdo z`kn_p75T%_eR**@)Wo*~28*pKt_#wW61$i8?pM?f?y0zGw69rya}JDGmVoVpM<+ zq|q9BNH5EOV@J#aDvQ|maJx0NP^ZzKE87-BZBQ-evUvV}TeR#2u89ztauv6^T43an zpzET_Xe=+t^gtddefhxQ{uy2aYa{|&x*KAv$j1W(X0NEqChIB1Sk3+N4u$;?U^-g^ zaNia%rTgWE$RQtqz!W9v?N&62;3}*!Za)$$;mLo2Q|&pwX{#)efQB_H9)`3X*VrgK zQX}+a&8cqbEN4ubC+cNuwDQ;_)C3sZBXG*D>75>jII6P=YDBhRjc3c8j>`1f^WIkIe zGa5*=96m$w2`3--z2MQ4ZfVle%W3Fz;_cr;eQK%^_|*c9+PNdTu<6u{g3gvUk!%bW z#i>`u>yMVwznN7~(X4SzQoP9pp|ahF?SoG@MeQEe*vxqswGl=cwM1a96o~xQz%~A}8!Bvv+p)N-#5g>PpQ#?A_tpUS91vhF2Xn zTcLeE1IINm>NH@((yIzg53 zf~y-;(*D(<*CT*#{^9^!?R$Ba^@!U+-QbghKqFR_a5qlC{hY%{!NkNgtV@eYQ!S^b zo_pI>%uP@6MKI+#(5V4i*qv}%<~s&c1C>ozpUJPL$fF?E-)R)ou!R$|HRTKx^hHTK zjQBX=wYpobOUllnhsaXH*Qo3=?wCNX{)kZl}QzSu{-hV)?*nab_HlU^bO z>T3d9bn5MtS+OzvpPSV9nPSkHUk(TL?oA)J9N##+(BqtGn!{wW9EGZuw|pUt zoFS|XK5>N!wZ8Kp^-Rh*Ta#-ld>~W*(2U1HzFI--Hc)%U@KNEIAS&(^dXpWaid$&3 z$7{3Iz0X@XfN)X0Bj&L|Un}wMX)Mn@D+kdHWBE{QHFFkNZ%744v96fHl%=ZIX<(;q zN?$BA5dn8)^9rG>HKlpWLU_-{@}v)-J6(&iBlO3R?WYOPTm;Ja89Kq3^QeBREldpf zWzM$Sw#OD0#)r@&FK5#E=#rTf=*J&MO-Yc|DRy%U(ncNXSYY zo|vIJ2T?E$eDJZsCDGWh^*O6sUP}*Wo`sBOqQe}y=}J_GP?KO1=8cDRz2!FD_#$$a z4M+}u?#rAqrA)w70bDbny3mX?E7t8vcBXAq)sbV9A51L4Z*eNtc~MG+?^hO%Un-^k z-D*kS71_1RCe^TRJ_PF7Z~r=q?H$W#w&;vV+tPk_DAVhj%}bb(3Df5;g_r^!rt0;q zJ77K6_jj^X5d%VH7L5vgVleDi*XcdOPR=6lP6)UsFuG9Oq$OtFC;v0~KGs=Z@yOk7 zJwJGn?2^|c=QMCIMP`fcD-H@XjSs8}zu}ck_rLDPZx*PhBw6l9H5oE)RlyW)Ku}JH zGGS`&WZ5T70AECuD?<3|Qx%d5nXe2IB+jEjBTkzYcTjD~*s5{+hD2ZxvLO_1N$`ZA zk8{t@!7g~Jx($s})hntHx>s4EeaO_r0ig7qnh?Rd=atOFy>H}Ckc+Ex4?BK~zbFlM zL!zd>FCEf$W%MNsidV(Vh)qJY={*vocih%NRHqwVUe?D-vRUTlMJUTHDw>bJW?qYQ-3>jyl{uTc2GiA{Ub+`ZS<- z#oyNBmRuIvRmmzM*p=V&FnTXc+FNu7vC0jub>cUYE%Tj-f(SzB?CfH77@KSP8;HS7KG zNbr0f;fuvctiyoNF~4*8@a5nNqbY*I&V$9%hiXoc<-EuWh|01b3>6OZE?}P8b@j8s zVUChDFRugBr3*s*cp;`+5}+bI3Yh++@}`HbRQ+5W3ZOqM3W)Xhj^59AUzk$;Qh^xX zX6B2O;hp^;li!Ra^6u&`@4y+roBW4sCDtxDXvI{A)*f4qmVDew609+lqaE9J)27(E%!^y`;{U0qzjkjWnYJ+fj+|4RtC zAkj8$BhvhIbiTHEiI$ZB(dimEJar?`qJJF~XiHeHMRn-_Iwoj~fCQDTh%4yrFw)vg z_yR+4z62~=S0E+{f!NEd;SGvUVMfQk(jFRvID*+@PJ9Bm5l=*p*~i{Fx+iJZa{N;K zPr&?0Gi45fOCJ`5bf5Yhdr}~D=dSXck;=AKC`=GvO$nuDDJfr-lumGG$_N#w>fK`k zK*7#o-~dSJ7Mp_dCu)vAd3;$po(i8Db2R7ps(A%$(k~FpvKm+cp7MKz=KKD{Rof5Ps4A$X4ngwsX+I z*4!qje6L5mEijDr5ZR^1di({n-r{Mp)xiUr$QkZm%V&zffPpMazHVu26fMuGJ{;;R zvFv%Rf1lVUNFMLm!L6D!dsdKesUyt__ihgkwgs{6)06(-zA? zdg?PN&H!lL5goWJg~+C@zb@&X&&IGw7^kkqRH}Zm95&2p#G!zDgdN(eO;Hwfg-NrV zB{*rX{>2hi?0X;!l;U{ijoM4O63#`n_ZXPRMk-$t;&czLjh3uC^=1dL>wctV zAbeLKQN?!7r~p0B`4LyD{sLFc^$%p4PlRa;&y_2e==-Y9(d@8>hBMo zA_QakR^b&!(*%m|!hW6F0lAdj1T`m@4F=9&eX~*qQ#I9z8%gmUVAZ#?Yw)?k?y17T zAfpmB;N`tm%z@&2kpsAqqz$A4)`X`!AoSe2+!zn3{Q%$}^J%!}{yV=c66qkmEb3K) zk>kTz&rMP_)^O4khL6_~qhk5n5NWjz7_WFf`?%eEzOy5)|9-yYfPf^X z&<+zMDDCfBkNIu}hDFq_s+*(~EU;bgDKm&XsH@DEgLG1j7njE}tSuIo*#OO$%>y6$ zrFOrGA`~lHySAU2c3?e^V!5VZMR+g`-xo)kPXPE4{Lts@jhs;&%Kb6QYd9mXyep9{ z&vL5$DDcn>uEA7)B90@R4OCh2cJLS{i=R&3{L_U0me7M(h(v|v-F`X zQ|}C66kOZ{6=+Jxeb1IB9K2mTodu_6$psnKMPGUNjGI$q!7*2B&2^;2Ga*C&!@B2o z&CHahA;a^SSCN@7VT0s!0%}kH3h(|f$r^ew!K=20$*>LS8arxN9qKe&1-0rgkO9Bf zHsy?=|Mo*=r~E>&0>92NKx{7nG!(FZ6e#$?@nWf7Pk2NSS(B2>6InZ(VAi4kA+bq0 zflErmsKQ0)_NQYync>Xf+vDC1-6$cW4^gLSws9>T`8C2PSZvDHTH+$*!s+CJi_ufw z<_(^r=AE|b+!k~5;4#;l-x@w$@@Sf7Z!@nDh+u6>wy?9&AKy@-)yIq+VU2iNw8@;^_pYISb4|q@e|pco!Yr z8ke~H?x^UMEtTTAE1DG!XRUfl=$LM}^tmuG<9Q6K$Y)z32e(HqIeF%2+AcB=_C~<7 z5f!QN-YV$D@fZRIe*ABs6ZxpN7AI(GzEUnW?QA^90W1KFE_kP0RBxG7H}G z#)RW`rK&C-?ov?BAd(@8rSzy0eyPvwxBF-$`9oU-s7PQTvH6(fqe13oClw6Am_gV! zgHhB;``5yOw9yGIz)`k*>@TsI_1b&Xe{9dOUie_u!e6h|gn4bpyUNx0U`|On7dPH8 zEPq4|jEU%WPK1j(bUox-tTUdaZ!4u@mg4P^(7OqqD;=})yx=GODGj$+>9$;Bxja@t zaV3VFj=!s=cb*$_PW)M?r(PMG_U6|9ia1n~&U%wAVBSz3Seh?D!s$8JJrumR)O|%# z)*H=|InANMuyPay#bvV+C2}XqjUc&tXd%|{7_vk$=Uxj(T?3kLH1jCbX@9pBaZ$t3 z6_456(kWA+jP-2B)%6RxP&fkyS>BpCaDlnKVx4ROQmaT{ z#m$qnDK$tVHM@v7wI{z8aKPjvId3D|v{1csA%aAHwP$`rynB@&UJMvJ{uH>wLj@Qj zmC=zP(hC$nEGB9i3io=!7Km#az3r^Tn#+cv^g^LEluXsJ16!yC?`9PPl7e~i_+_-d zZ4B@O=5p}apSWSWr&PrFa5T)o7zGB+K_LNNwy!^a8z(qVc!NUe#0{5;pQ`bL1*nf_ z=kMXe`_j(uAi++mFGe_p_i! zbAL~Cl6e1Sx8pG}>n|ZmVuibe4cqHe5~1L^-~nR}NI>tNASA28Diys7z6116zO%2| z{?ITMX5Nv^MebAQgUL~f`zZLWS~|G{Ox;>4-D-ksXfcKa0Xnuvusa=O@AbN9he_;J zAwpfbCh1*yn{#UnAG`0~mJ7nSvO$ZS*QjztjaP^aMJ0VED%-Rz8mV*U$cvPF?U3wG zIF6?e2^~RKNyJn)2k?WM-K;2fWs`awi=RFR$2 zHKa*&;BQ@P2vV2Z&GdSFUp*Dfo}aif3eUW9+pDKDiNm+qJV(3eX&;Q}RePHJPGOx` zT)beM!aSU)l%-0P1)AvJ*^O(y(uPWb)i$P{0gbkbF6-Yiu2wQ1k?!W=pk}cFzVp)s z0C)}V0twzgfFxu_f-_f`RNqBa=+^_&MKsb|6ehPp-$nATm`{5z% zta{;b=`-iE6P0T8mVwem2~dO6EjA0?^*JtmZ~c$570@|kPim37)bqxg@Bjs6Qw&*~ zcdW-jCB2h?@xR=AwUkWJ>KS)g8&61ve*?b$N}B2na!)y-GnGrrFJor~k`h3XMaL&h zq~(q6fJAdA$%#TA>m74 z_tNCUyQ|k!GT-Zr-Y>IZT8LDo=?Z0l;K$d-Q(|Kqx4L8;0V6P;ZI56HmDiBlRXnFi3? zDV^+HOUULkM~<=s?vu71rUN9Dq*_U_$w^B>B_d)DO2S|>C4kU?LRZR7;PCw_cK|)A z-%tBtk_kE@eecHkgQ)1B+4VpV0Drh&U7Ea?{TnZy)FArr|34^5|F#L=OH40heuZ%d z4YnOX3`_(QTTEJl1=Z>W$X<_q!tK|o&QpHI$ybnUpCAfsy`vA@LU-)K-}hc8dl%3v zCki&c96vR^>;nFLrd7bfK;hl3@Lua}!>rJRa@|(sb0_6*oktLeBSb0w$6pt);+`P^ zlnjqJ58paG$FeT~2cSGwsEPt82m6Zz>?>@pnL?vRRv}*4mz_z{FKEDaNVWrE`=}vu zw4rKT_4@Noaw~&6=&Iz@0wx}$eefmiR9u?flA_K%3w+ZH>)q5PPTRV<530j57*9~d z9?1F&K#JJkkvESC2||$YV-$2|Eiu{bJ7X`}`trRQ0e=z}M8hM;113CINffhg5V0Rt zSrfG0AKmcfMex1I%ns5;%9+j-3{nxm4ahP;5xP`A*s&U_Zr-VGvjoa{+>(_o+oE=* z!;{}{Z~5rnaxthuQX~7`n`4yB^j!iA3+#m576D_qFQ4n1ku4m-3CU}Kue_p~#B=6r z={!hVR>-O^9RLrqB;Qsv&7re;Tt6emsy}npQIZF9$?_?1ngPA}o6#Q=?(i9xa6w@X z8oE#*i3QM8f6dOyAat?c{^lsPTkkv8W+ZT}S}F);WzvxMCon{!Vzu|u`-w{PUe%83 zBgJb^X2s5xW042Q^XcF9HG8_gi(|74tP>g5B4-h`?y$2|GjuL}Rn01inaq;jy=|bu z3(Ytm$@A1=?i07Xmh$DIy$lNta{JT;JzHJGT;~{V$7(`#OFR%xln!r6pwoz9^&`L! zKKk3P|0;?;I}e-_U{^eAOT?TC(&C3&-$FB#6UrHyu{$iOA5GhI7Vmh(ON4Lc&5sm0 zhi4=PjpCy~Mo)!pMvNIzAIX>}W115Orr$|ClL~?_wQCjXa2VT6wMB7q$>A`&y{h1> z4QgunrImKrXerdMiko8B?##+q?VuX%*i~SPjmzeisG`&z-G_wBq(gb?S4|dZ+P2rc z8V4zV>9PgjMUJlt==*%GP)O@;>jUe?01t@_gLqTkyyPWZBUiL_cEvcAYUTVJ zN6&W4PUoQGo8)3v2Fcx@($KC;b!IcQy~_slB9A_L6x@Bw70mA?SvWWsb1F2UMy4@H zQsDwLM|Q(`PN(zG^-ZY$&*S&v_i8QBJ}<%*(GD&wV&QkGU^7aM*A^}xrN)Jip|Zv8 zzcejP!joZFGt2U}SICRPM)4kW7%lf{BbcM}O?XNgLZGnQoEj7!Q>ZNis@po}ORPV& z2a9H5z_@wSd(;ZU?!?l8wU(_@nr}|U6DyKy|Ij+XcYKGRG~YMO-FM=K{a%QUv9fpq z(j8GZq%%uW^dh*^Oo3Ax8-vQM1WE}zM3V|%$o@i zO?y*JvUueZ(MK%S3!h`I~gd}7MQMRdM-?HzfB(g+h zPfU?LCfTyfTDGx|ee7f31~bf}4|c z{*v6v7P8;~K9O?27Zpby|M*d|w43QsA>JMlVoCr`e9Waq)|6BG{q@dAd2()pN7jaY zIsCuWB=XGTD37s1oIdr#K$C>RmzG&e;kkBlP|u5Gk}b#-+d7}IaqA#2q?{0 zzYc`|a;?KQYP#FuS$cOa33r%LTow6!HhzaU5a}_#-3lXe%@-v=1D^$P>-Vdv_8$g> z@3{O2H2n4V7p>%G9=UhFl|uLlmVRHk|EW^&=O2IGDnGEnufhEecr{noP z%_*`Q5Rq#+D`cKxGJNgTR39j;3mQIhc)Ne4*|?(#m)GDo$ScgXjEmEw!IVb*`*!>B zHLBL6v|4jg&MjJW>6t^Jnooy}ch{P@QlKUPgw9%)*bOMM2$>!i?$ukM8d|PeTz{oI zUPdamAF@NXk;(V!c0oUz`FAhOJ>+=SR#rf>WN-KW=@t3iqC0B3*JsccmN1#$Gng&X zE?|*R%M6S%%}d`Eiea0x4MeTiS2z02#-$_x1kZWM#>-hWTH?sHyEWtpLIW5m2NY)o z3hbYqakVdxH!*d-$d1~p7!OKTw{H!DADlW;?y)eSdbWH)imzi`mAiaFdhg3KXCU_+ zU@AXh!r3+=^0izXa6>5ocH})KIp-P^kB0F5Z`NXP$f|vcx#?Rm061Eg z$I@G}(at!i;R3W$ zt%`8kuxj|!lheGLHlSG3gbVWmC<>gy1>C#O@)InW+Tf*l9xnBKzE`soNC(-S_Dy#k0${(@31S747@)&Q^zeYrSYr5Qsf-jXGkwwSk~7l{xs zll6llkV~~Vr|orf_3#EaBpl+{+})DZ^hVxm{XF2T)!xKG;ObJlqucVJk1C%9>nfRg zcUM4l@qXEx<%T(LP24wUTTvM&DHx0iINS>2m1|(M?1bq!X!PrMv_CK=xE#o05Qn!D z+-hBgbE0#zYr2IFbr)V>?C|EJ7~%XXXTM8sOUvDY z$itn?4eA}?cM6n^TI0o3*bycrUv&Do9-A^fil)JVGEQx&EKuNlYK}n=gsMYbQ0=`9}HedP8z*rGnb*kbRujkn+qs5;S6-S z9Z#MIu!@1L5qC7I3z5?-IO8g$iE=5^iZx0c_%`PlV9EF~veaoZ2rtnnwEvaPzi=ox zo6#S(eJ)#l{R_|`Vg(pM<8+6S$xKiO@&47S=_Ytbh_2;x{EWcRT#`i_h=1?v{m$Cu z-K$dp`4{HATFDi$woCn`!D`W$lr53aV@-ZL{`}X*2wMw65mNBU!{uAAm!HG3Gf`}! z?y_=(MHjt~ch7jwr43F@2l<;EuHHB)>G_P$aqR7vD$Q}fZmCjGY3zhAa{IjBme+^) zcVq5cB7XC&UZM88+%cE7qdl25U6Kv^=8}H{a#NyrZ&@wkRnrLvPiKw!+ic8wMT=(i z5_!(w&e$ab;_8g7u-^u6CnZCCt)e^CNET=0ybEVoOXjA8{n_T2uUG~D^0(Ki5J*aJg9C)71s&OoEMzz z5o9l68=)(fOPZG|5<|c-Wp>x^xMN7;!CZR=U6JXz=^iu9mSS_!mec_qOU@jApTIe^^E?`FTEp^@VS@X=&B zT5f_{8Rb}}3uvL+&8nXT+?O?d^G}aYI!`wzw%;LdQWg5F`0vXocU`gwUBKMJO1mvz zoPU@oim1|uOM7fL>Va3I9^)N)K`TKsqy0@Ph)`~ZF-t&iP`rqK2syz8i6pHt$GV9FPk)|c-ZyfgDF?{an3?<$aC4|7O}xBGy3t^v0+&aeQX+k8DqC!Q zX^PCd@wq-IQpL2cbOzp%iOfipf?+y_xcj&C7UqPDNT4N71O1MUJBHq33L4|R=qfaP zjU6K!B3LfVS00~?*#w8q-fY)T6L&RMXQE{J6)NHH_5Ky+o&82+;a!clp1PwVmKVbh zQBM?il!(ABL9PPMs-WlPYL6G^v;ndqmEbLPg7X&WJjHNz2eim$o=JqQ3rE&4aEc7O z%9j_uF7Y5MOvblej*MTJKJRY-rKy4?z`LC;vG`=uf`mglC}{V3V#lkuVttxFejb}y zWs06l{)wPpOmiQkP<`J0wR$87CL6FqOFMTCuTwJ8$TisJ1srKFVWg~6zM4b(6trD| zNG#q;)Q#x|mAI2$I-RO4e|`t5xqVpmky74ABnq#bOS7KiRA-n_<@t3}&oypn@JG{2+>)O7&r6l12@V;uA*h!yPPe_714=&&R@!Gf-p#$B z!-8t{)ss-9UJ70cdue*#b(+ucY+Xyv-Vbwa#Oh7v&L%DuO7 zU9y{ONoW{ExGy`VpwuH2@Qyq6wkZRvYg)G|G!0GIIKG~8%Mj~T&0QKQ3F9Dbdk@1o zB_Gy!z8wE`P{Si(^Gg#e?52ZJ62?=OH>dR?<0Mzfy2RXV19O&wp(VIVmKG?wAAYeU z%}@&x?7g_*gTa0!OjR;^iprbpl!C3kA&b~MqfIo0Z)w7qWXl6o$h$?Br<>e=i!@P0 z23BvE%O)yf$uSoVn7cbSMUHWP;gqv@^Ve*JJUou-Gpno2DRE!*imO&bd6kk5-Ni|g z4n3LqS#P}M%jFI%=2^z^j2C}2!Xg|WeRjg?PsXdP^}h5eA@D*+{ie`aRe{iyRgpuZ zGFKw^y*U1Lgk;&aZb9;cm)5rvJw_AuK!rw5dK(UQL*=T9eYcc?=?4V4UwkST63_9K8!)}(pv9Rx zQ#|#2qsMzkYJ;$W^T8w>00&EHcI1z`%NqlKJ zgqQ!Ev`yaK5k25+$MYW|!YkNk)YAb(P_B#h*7vS$(k(yO86r_Oc)N*L%W@=D$09n1 zG{Pm>Zj$;ak}p=I@ zqLIyMG&9nYda5N3-W;uJoX8oz4e?(OkDow7riF#L$DSYPb)Mh$6s;^%^e07@_Vv{a>(cI}zn zQ#+frZXk1V$a3)i^u;vSo@p%0w(ZB*Qx&>9t$EG_^YZx zLGF<-gR^VN`|kevy&yz|i}?uNLYO|GInH|>DQEEfg68Y{^{<91);C>y;!Wk#CS8&9 zqRjkT=PNh$&kcTZ3r;is{IE=d;$M$?wGG8iQe8AnIj@l@9~A+l=LK=Ql$n*{zb3xf zGbVw?;ObeDBL$~qJn<*p=7(c!db7>=sB3a5{`i4=Fx{dJovNS|bZ4DATS3la95kHg zRM~4&0*aE}Y>9-tW6Eehwbo`-+%41gS~vfGL*~Qrvpw&vMWr9kX3iPc1x3E^N^8+h zmh}=qbs;iR8#Vu>I6Hj+LZ4Ol%+lFPfJB^ff#~{d5}*v72Fr_g+S@!!$--t$-33T_ z6;KE^CC)Tkz%=$Ney)H8bY`t$+~n8GyiQ5Yut}6{&1cG(%WtA#ZE{#@+oY!FRGe-x zU=*d6C&=m#KMD8o;uKrjD&N<6@NXg*Bw_{9&Y(YB7a`kS@>m+M1iU6yzGF2>58+P< z^xi~z4k=A8-_@gj+qc-xndTbO8Gg1poFwZP8^JE43U8|K`US~OT^pt!VI+jD@p|o| z*q-@Vn)>C+yDhIjf7amJ`I?G>Rj|vh#!IU1#XE_}>Sy-$QWBHXBsK}JCLGZx@bJox zhDWw8=EQgA&`FC}t1udc!bo}ICEXT#o|^j3ai2rxR+d&(aMoE@QM2OKWo-tRJSbCW z|6Hel4WvEp*?LmAL`m!!KsdJHnWDdufvo!?&;LyZ(!_P%G!tn{9RU)yaD>-^WR)Q) z4u~aZk=0t&bqPB>bCEqN`IMNq5j*x((0~iZD9;wu-C7xebx6*zP|RPKgF>r$B(Mk_ zKwnNY1ca9tNuX{bO8h4+Qt1{y`us0*{WzRh(CN3)=^Iet#dV^)ZaX6!VO$hznu9Bg zPZ6XO_ysG7CR!@b*Q`icqL^?qO&87#ulI>i`j1i=A1U9B!1$f^CLj}|kYI_xDkFyE z26{UeoD=OvVy^&8KwP?J;=THj!Ni3HRFr+qfpu9^e~Hs&kNyi4j3T!1t+|6YuQT18 z{?eL*({1l^j<5CKB617gGLeY)=HSQ>v1zrQf_qIqmjE^hF7L`OY!moRD!O`Mxzz<5WF{WDVQQT3xp?q~aahA+4t`}1q zrCk^A8-g6C;R*}WMgqgh^Wvoo-8dts;c_j|z#9m=u~@Z$QkYmI?#v!t*nP^kT-x1@ zd*!kZsvlAFV|@OdXK-?N!3fKJIMLt*c=ev@*nvc=o1mmrC zAE9z;6_UcoXP_1^F$Y}8Gd|KHv3P^<$P zoIorn;$i_1Sm_?iMvhd@dO0+mcH8&2`~z}~#`hTCu2#si8+WS|B>&6d|B5mm#;Eyf zXwNryjiR(gT_!Dh%e9Ol<0QDuqP-9Pcm#k)=c)f9pME2m028+x|BGBkg%h#}<75+C zWHpprVFHZ-0=dP#JrE)(#yZEZ20-%yR7gL z>_L7Z%(~9C-sjb%lt>Ir-NP-5&#LGxWOtf?)Swnje*b#%ez><#I3a-EM5LssyAreo zKECKjWdyX>Lu%f?uDU&G5M0BAs8fWc65B$1EN>`$wEq}k8$(;@ev7>UX=O9TU1WpG z)7Wd11(fAid8U-qB5t zT3l{Wz4Ph8(R8$?w@(&kkKF^8Vbb#FNqX;@1WO>oD_vUz$+AF0H_}}mE(IF8@o^Rw z%laM{*ZCgM7iwL#Ws}k`NaqPU^&kPo)`Z>K(Ge;`u1(hTU zt@6DLBl)h8X~l7%GPl@$4r$l7;5nUNo=}5=>D?P3hCe0j1$daCnm1>=;D!PVzy`&JRPC3`Lt{5nSR?@^nhwWUu za~UWgmlRN`mEpaohOX26j)TVc_>|6QQOov6t&qgtyALe)_ZAT@x(l1)VB#p&$@PYjJS2IZBi(Riiza4teANS-kI_6+Mvvb^Sd0}6u-e>F=gbAP

e$%MNpSSUW+x)T2vg4|bM=-F)aBSqrWNtYo)d@507Wc-YK|99^*nQzM=%Z~HtwLw zqzv#*Od`28q*I&iANb<7Q&ZZLu}$H!`kTUVR$G$LuJdz4Ar7@$Dv2vC=*o|vNiEHm z{K)*Cxcn_cyA@KTI{B-t&vzZpw3UqKE7-ICPF!AxgAO)3OC{)yJfXYiGMe-9qC!w+ znzkg$n32i@XH8*nDHjV}&XNSu{XpEccyasJR_b}Frg17HkLBU4=qf(JCIV$N=!~R~ zFwG5qy5En=l&O045m{`FF2gNFBHiu1M23gR`fU%_%!_^7>Q1k5`?RM4T_`Mb&SnL2XyW!~CM@C@6G}4Qusg=?2^MeXtfL6BTRPU8!uQ^ags}MAmG=pOd%9jw`Zb zIhkGZ;RL_(t)dV1Vdcv-XB!k*`1UDME4(z-d;jF%&Bxlu)1HnRJ?c<=`6}?hvFo#y zJdDS#b3fh)6zuKAwfGP%;-y4OxxdkQ)RG6#1+8yf(udEmW8n!d4)3|P5las;`43rO z?uRi878Yt_JnOOyk77X%?BfV_sT6*n)$Z{KWfBg7y zPp*-Ko5_~}4%&irOCzZb^08z&G85hnW_80=*|78HVCE??NTc4coAgo96T@%WvGP&cFZ$- z;P;H+HBS`o)m0PULz&0X4X23<3ix0~Lz6%l_!GtF{eiHsC#wkRaV|w%98@Bjwx`r2yFoTucs8fSVw+;O>RkhfpVVK~~~uza+GO3}kvV-#uC5bD@b#{hYS zb1_P0CJ5eIV5i%RD2^&}TYPoC7P9vp#(Q6XuDR4WQ6k1Xki0)&715+NP97UK((ck~uWeFye#jc0oe4_=);A*S9YZdWsBMmx1vtYmVx zB1~Rg{r=M)@$=f@sSS23Yv`q++u5vOwF)_8j{HXTgw9f$xvkIh}Ux z>05zaiOSFvjV@?j91k5_2VT@pG-=Dj8{}R0hA4bOBNq(IgRsS&o-$uK9CdX>8!>)anJHRPv2GR=O%P_MRJ0^wAY|2>ot8(Y zLoLRWT3(AeDs)=jw=XWZ)jlZWuDb3Y2(uhpu_D{FuzL@dr>!pJy39#@B3CY@UF#La zPko+o7PXm|xZU$j(kGe5Vo2bP(STm5L#d-54K2r2R9+hEy#A%N_x46rX$vUP!g4`z z<2BsaSNXO)9`G`4wfk3vAailbcn@K)RzDmkd9{ApQmwiK>4dnS)iJ@`+sDbd+2ck} z9^6T7I@hz4Ia{EYqi?@f4@XfmaH{3N3~eQ;0^9Mcbqu=R77_1^J1E~AUU?7V!IKIsWP3VTFkEoTs(Tri)NGVmJoVIXh{cJAo$0V+0P%Q4v69dOO znrGBAkUx(x{S?y}s}bJv?$KsdKd3qhPh&oG8j0 zXB~&t1(`)=apz0nI(d(ek#I{xDxjXmdlfFfXwZa21r(pqB~(KFx1U}MKdC_8K~>MD z#hIpL>1JfR4Zan37?#Y|Nux0-ch?Xwdqd|U#=Mo9dswA+vjbnJ=h8`$KkOXCA|T0p zDHg*qa(yNDRC;l@^IV^pw|lLR>$pv^q5D!h-A+@Fdm+#C`=LPC%<>3ZVSPrrH3ypg zcG<_`yjErljE**VeRpl)G|JnoU;>T{J*6YBNBQ3ggt35q4>lsai6~@PQLmjh`!$L@ zd6cKu%^@cjU4T1XWHa*GeR)(JN>6P52s@M%(=?)$BwnYTml-~Xx)_WnHrvG%?Cr(N zKI>p(A-c-k7I=Ahf-h*iz6uJD+$2;An>A`bp`zpE;|mcH6}4U+`(l%HtjE{SZ*CI> z%~)LYxo7lc;4d6zt&I=Uc*0GN&}i;$<4c?W#*;6}6ux_O-zP6L)O_B2S zClUOFv#u95`z3E)Dp$u>`w=d3K*}D8JK*cJ4PNk&|NF0v;KexGkLtx9CD9%N=NbP~ zSn?8_XPx%YkrZ0%gtNoAC5bD!T%vtO5;N6GP3@Q6-;@|4$E(#*4)gKkc9XLCnFDtr z#E%?Bwy4L&LH45TT#A;{tT;Gq{NTD#>1!Rq_hGC!h!ysoA$iyZIlXJl)oX9YJlpnb zdT7amDAM+~U9(8J^evyg?lZHqRvKEK3{~k03JM#|{0ME(nA=$3p?K@=c<_!Z4h5}u z4vSH0SO+WnN-o(UL#P+JbHcCMCB!g&dJDGF1?7QV&nQ4*yE0Q)cAR!Cy z>F$D_lZQsi+|;|7MxPAHVOvE(7d&+Ht&7`FrIrF88ZOFl$S3-k+A$^iC z_bp^IUj3IdKCb1#cFqA9-l^~%yW(t>GvvgvV=1&`7-0y#=Sp=2YIIjw^QOLk8^OWXV6VV1%&aRC#d-$xj_E(%7-Yf8gLn&#R4bcifSwReY; z<+gigcj#mrUQm?Me;=8cS>q8CJZuZlU1&5ke2%=cfNk>|PyHIgS1%#y5v_-~5)w{ZERd&B! z<=MM~O7x_Z)LBrC#K8H!{m6+ zXL6ZTMQ8S%pek_TnnDDLloX+HwSnZ?&{gOGFmSII05E-gU?V;Ql^N&4 zxhNkFmhxb5EpE>T?am6#n&?Y!H}niXA?>TJZcxCZo~$yQDJ(lg-uv9cgMU8| z26|8!uqodDg?X<0z=5JlKN9yI@~PaO zG3v?JWuX~4=3s@PNjkjIUih@5`Hukt+(ga5m73j~9R;hoCJleBpc0YvR^E@mHds0c zj=&BNH2KQfVNAVOU|^7La6}T_(sIcm@~XnoAHP12VjMjI<}qIfzcP}+wS8$_ESlfq z!F1Q^(Q^$<+cv$n&%nJpFLgxd_UC8weVcEaV{$rPysP6AC7k8v&d>p!l=#+$y^8Gh z+qrMXzKFgrsR{bN^NGo|XgjP(s`<4y@L#rG*UQ$;pud%^mq6t&_N+_fQ}52=aLZ)} z;sFrn7vnEg2rX5`Oh5a|JKtNbH7b0u+sLs}L(Y4v&yOk)hJX9)pYso3n9FT>CN@W2 zgQ=R~EEkr}Aba#Xrsufi%s!WsGdl%NhZRk?lK)ceq1!yAshh5;I}{Wc1O!dkeMzdJ zVtb$UQ{n-d$)Vys12Xh7z$7fJclzSar6hILG^9Ks;F`aLS}t7H{9=(U`laTzhG%D} zl!83xwsJ2?*bly`tt|oIM?B5=It%<>2MYbV`02;EWPnE_%uNFE$@mMW6%GA`@ucwqeb#M zRule#-7P7u^u`^`Uv@cF1}jyCHf_?)m98F0dQG7xFhGopp(;=YmHg1+?cLG93dc);1<8nNud&1 zC&8SOVn#=}`N5$2u@V?N90U*{Qna4Q6r750b5-!c455E-E`WkM&$!r!uCCnwfyuFU zWZ9R{D|cTU8fJe%*kXIz7s6*FnA zmF(dG=u2FSD3J%Q#l*DuoKsG}m2zy>X{OcWbbFK||YzoQi3XYZ<9CVtN~P&TC^aZ=|;?! z)v9ljICR^zuSy=dFV3lytb`2c@Kw-KI ziXIyYlEIO6gk%Y{O872Dg37Q86}n%7DrZx6r3U!O8@zfl*CLkL=nK!tS?NCW40~%g zfNJnPSb-$&uV!Cf%2l%B{$rVd@UV9Fdd>9P-MYwBy6JY1;&nPundg5D{iw&u$wW7= zO*YR^gUxAa%R&$KL-#sEe{N2&6ri_{lENsy$Md*(&qhCg6KI}`zERU-M&`=D-`0$^L^fE9iRO z((Ef^Xcmf^q~AY^(#K;M$H9s@;>`ORLO$uYu6i0UR4Z;47l-m0VvOOq z(!4A;`=Pwv<-U(eABvx|mb-K77FhUiU?7nh)w}jIo68(;Jm?%l%RYX*nr-07C$~K% zc@vdm@WRlNcDi3nPAvJ<<=fYWN_ghx*X#66?)4W7gxK`lz8)zn5@hOQ&%C{z^eS;G zY(UDS)H#vrNdFBozDdt}BE)Ip)LK#3{bG}Qe9eB%5T5pB0-XFN7Plg(Bv`E8_Bw2K z#Dk@!VZS9Alnr{6^N$~=HVzy3bbV*+iP#3<*eo)mZqDN`D0uIZI;cJ6MGOqJ_%zi|tN5hw(t?eOX z!D{?jJ>u@F{C0tuLxm!$(XPC9LX$wOs-fXI~u`^v(| zq7zH|3#0{#=V{b{yRM#p-hxQPn#^~ZKk@Xd)Z~C3ojHLwesBV>aj0Om`RkBXNmyhx zZ(dz$WW-Sk*Y>3H4+HBsdmfEJI1Vc%wt{i78%1LF6&dT7KW8qDaGY61^He|nBH{F4 zYANQ^LE4B;C`sfoiG<@TG*=JE)6_pBinUxTXh#K;r`!a-e951hE4|^nrI9${>K0x= zZUm&yt>YI!i9#KEzCVK>`YWL>Gwm5}3zWRuie>Li6s~>_$XkD)cSOLXIKZOyjYgGv zZ1)-yPp`BJY?Phf({BtlR2E&gCQ(Zy@B@wxKQ z;^Jb(qps@?uY}sEnCO++-PPiftq+A-JXTjQii>i2axG%`OhBSe)8m+6%J*p_Y&54G zdCYP&X@h;1z|`{jZ?C&d-KSh%!$03o|H{J6(5{!S-^NV8$x)5B?!tpAk~jb6BI|jF zkKvq^iDhosxWxG70*4Xqo(!#z%XCLr3znK=lehymp0jya&-Xm(c^|T7zt8{4X~E@` zL-Xu!r@dKAZb-Sj@F!%d>D4J>&!VvMD&^wr>_aKpsc22m6Vpl{W1;)+nW)% zZOoaTN}soR$;f+){c@KrS~)?VZGkfB#sQca1;yA!0M%2KW(JS{pw#jW@dmNoBA@tt zhv*GUou7kQb-iG$Fq^p+^6jz*X@96JtMVStv}PPC7%#Kqg0Yejm%)j&C04)Cx#1A* zG(fD7b~uN+b7wh2|HEwskegSbI}YKS8^ya=J`|6CD4DI_rV<{5E|+M~woKP)Lgf6S8*LGHi?zPbCySu*i54Oc1MJ1i5`*UGk^^|zw$ZJ>h9Y2nF_TFU z*ezo&0mlmAIC>7Xv?tU;Q!YyHqKywwP~Z;!U>=*Y$Klg|iMjN#yX_h~U$kFE^N!NQ z%UKHM;uwxg9FE@U7Phn=aE81VcA*#8GKs}SitCIZ^wa#7({c)3aHi{bKF0%+Rz)FE zk(VFyYP{lftCoMW|5(RSC8- z3yM>zw0GJ+6uM2GeO7LtF*k7U^Pl!aco zDYxyxoa}hzFrKE_3Ex;lCL$RoENHbpT8T%Dj7qIlZHE+})0*>|P@u1b?Q~n+6e<~a zGlC>L`xJ;QNeiJX2|2fC*iI9@%%_&xnx{62&3G5<$fgAvqG$`#mG$SRW8gz$GLevLoEs}}rz{}F>^`3i?wI)lllLz={-POW&+fx^cQC!osMkfl zpQ1f}7jt+K*i@}SuKRr}1#f*p`{BOJ--llX_@pvcn0`pNd{?F+4in%DA% z@+aS%lwJi126OXxYticLyy$O3*7>)1>JZ+x@axz>m~ZMAyKr~^uT7I|r*XEeu!X)H zx&i`!(>4-j(XqME2xk)T)veJ#ZP_}b3YJ{cI+jSy9`41qjcr~Ku^>!cLA3IGxo23; zV5%P^yUOIXttxBgexvk;DH0J5C-OH%!HEb{4({U4GK)6rqGYEn^hv;phk^rtTebR9 zZFLw3%FK#Z5xk8=1UAD3(|u&=_;wqGzjyWrq18J#2n_7$*sMm>yEet1`{*=R!R>&u zGNeW%Lt!-CDz0gX?ovRU*?>jYjJ1&}yvJYDg^S>cXb7 z*idz@xP==vy)h%AJUEyqG=Gan%OZP;ih(|Zv?~!N!Rtx%Z;Dt*%MCI0ymyR4DuqlW z+8>hfNM?dZV$AVdLw^Y~JYp_bO7?L`kz}a)Qakh&!;ff*Uy4GXACnNlTkJ>Q?OJ{k z=d~yg=OJe$@#+j+yCxWg!tzf#-W0}PRdZ+0a> z!f4+9pJAjSAY}?08{nz8Z2LkDHewPFbf+Wr7ABe6JIm-EPc(k1ZBU=>nG+SqwbfC2 z`Y3zB7zYd9YLVaEBSOk4ZK8KRGmPq(g? zocST9^)gY(@?7=^{5Q*|G%oeJ&^*y@JDH#-psnqTg~4KzoT2s`K5nu>@anEexG|P4 zG|yzW_s+`nR%kQ(0W~((v=KO2*7OGFS44S>+o!Gh!Xb=`zZ>y6t?bP+5N?nYF20Rt zXPNdDeZ=!+QoG&JfVYmnaJAl-7+U6zmbKQ_3n;7e~aYJRjsHId}#6`EQyU@#N%opG9B{8YLn<#bH%~ zL>>e`k$~PN^sgA=s&qAf07Xv+W6PgyVIXm0-b)p}Ui7RF<=rG^7 z*P5@X-lc{nY%)wk3aSQ>NvY<=%_p`kSdjKkQ=WnGluCI*e9k}0;W*n0gpBCm4bY%;rc*5tQc3o`lPzO-;O=a}DWKa8`y((WmA z6tdbKG^uE%PAVntVjRT5+4&|y<&EA#>#tGRnv6F^y%|Y7S#aLF8i;Jog#!!S6vJAp z1&1go4pRPNMS>X>MOS{vaaX<_`0>yGlMeTPAH1;qGg$n8dLa8@at_KdfKjpJ#6O?v zbe`CvHE=qxBk6C=?f1Ch|K||C2V{Rd5Fjgt!Q*@6bcXZvMeWH(jk%1X1v>i=GdYM@ z@n{A7Y3T>gSd^RUPc<)v{EOk?lJyPG*3Z!-?PHtswV~pWau3+qSaP(o6gx^fLmUB0 zg$@w1t@{?GqF*PQXCk%!8A<|mq}l`SKUMG*rJWc5GDf{lCH(z-FZ$gtLhFInYuOl^ zo=m>M0}e;hv%1rDfxT&N#KX3`n4_GgM;shLoyDN_yIz)#`6+zwX|El(>6B! zAB6!0{?f37bY=Ln_I%;RA-jQ7nr(?CpoTa9s|?F?hNa%Q<5rc? ztwouUYhKLQ#IbX|a(_`0Fs$ww6jM@=U8T`_dwJVdQy!_Vgq=baM8(c+C*8ZX_1}8Z zHH=IuiFa&zJcIjk8QuaaNSf0rbO*q7^zGX(9{0ZAo}K<-;ocz$qShAWl?vJUbCja%qeb~Iyu z)60b^K~#62SAVw43H5U&9o?e>Sv{HF&N1fmARL?OnRacsk7iH=2FVb@>y?a7dcP$j##ThU^KFRIZ%zgL)9w=k$Q6zW1k#;JY&W zAN~wgTvFtaqiO0mHpgc=hw8(i`aXm^GJo9+l^yjmd-yEg<4WF>kMFbSc0r0_#wOc- z8o8Nj$vT+7)dDbVX=a>_)qPl#5mc5aA=LG5SC>!nV}_xA&tARp4Pc7U_V$(rGUjvl zr9ov-%%iVJnBaS`;iaEc*R__`I=0MgEv}=(QGyw|(+4Z9mOHPMCOm!}M%CdV<2Tp5 z1b)eun2_f#^|72FtF#QnD zk1)bU3$gf6;?N2Ml(hAtq+^jO{djSoeebsgml;9ECOJSFR&-T+pcQuB`!IADTbKj) z7SoW;LOE~|ub^=?R>t^u_T!Z8(QbM_hW5+T0>vT6?m%DSdgJmK{@kwC< zd$zWtJ8drzaqj4~wKc%Aih6H!s$>C40hDBsDq05W$GfBFu60zCHibYEUYyl@PnJ!R zI~RziQ-O4IGGb-XGBf`pm%WB$c3yBJ7xS0oaUKF~#KEv>FBOT<3<6E$b9+V9%CH^^ zF;c1!8EK!LC;Dp={ryb)Y}_E|N@WxA@S~2hJ!`uGT?kAzC_i_X`=_@9aJ;ck9`2Eg z3&daKB71aY&~wJ`zK8`$Bxw| zss_bhAlkl~oH{iHq~NEH5{s0zuV!AHQR0)nKrXRbk0NA&_6Beu&rGjI!2~fpqr>7d zGKr_eY|cx3V<}+d4fYfmEE_68bgiY*T1^IN9dSG8weILHSe$88BDgu%9em%+Z2IDf z0>4-Rqu64*fwLr^FXL6JQ5S4R;)TsJ6`oKlKBLPG(^`BzSZ2_R>yt{6|?t6)BL_{von+r4-~lM9}ITn#DY`| zauR7huKF3gQq6s#p|Ij;a)?JeOud z*u{hf-zK`P2kU?`rXT+yU;&_qAnsZATrU#ITZxlV2y0>5$L;dlX!ahIHip-iNOTP< zEWCV5^5NU@Ttad%F zE-ja5zI9+2dq1&%nAV21yVdcT^bC1Dcj5B=ru#OZ6k(>@Nwj7;?co8*83Dx7qR&1f zb*XG2Y#hFnQ}1qW3JZbL{L}Bkn(e|jvJ!*bRbaq-SuUUP#H5@RX ze$ER=B>0*IVy>;?a4e`-C7qmcaAH$^y_F&Q$^PRQBx{v~h8CUk%3^jF~4>UkS@~$-iZJO#!L<<&qvh7$eNWwW|#QE@u6M zzr(xbjKM-{B>|)2%Ym?}Gk2M#ljxicTZ@ZMOcc4X6I0LbeudfdD48hinb8WZIs^9d zva8mGcz9G~Ut+u{3M*D?Y4>cICIc==IhFD8ao5fHTlvqoK7%B1W=DqeJUn6d4%P)( zUqBGZ?ja!|0BCUMSB4kBA6s@Jsy-s(mEqH@n=yAb&WSQ*65mA5#X-sZq$#(9qe1X3 ztS-!d2jU!zI5cZ$mlRgu2F~7t1=_h|%WhBVFuLTn?%A$f+ljWIhy_@5BB-Ux{X6tC zNLYe5#kP#2hlUc9K^r_J$tYCP7ds;_03S!8{Zk(sS%ewP4k#F5o{6M)t~*+(Ud_C{_59GgM*gasC1KU^7fp8! ziYLtlyYDX;ujdQ_KXPyOEpA=4sOTSWA5<8VU3QkUPYTYJCe7Dz$hd!P0B>B4;y)DhZ?3!lMbwQv zHxAp}(O^`ZE&fA^jR=Ho^RWdomPP$j>-gd5`{Tpk9gly@hCn(&DvTVRHqL8HY!<17 z?faLY3^Xs7)YQKL)twZxQOVMLUmNybKXi9I9d)W&e8-QSxcFXzpYgI%U%c= zRF0eN(}NXRm(CGc7=O6;lFD7*tyGa8Pk=~&yXOeaVLFy^V~03+M4*v9n|$LGpKg|N z3+~8|hfBJ~VIMK%fr~2*xKX=447l*_DaLU4-&6Nn5V(g%-zh35K0XZ~IjDVdanDH$ zF4-HD)u*mnvks(a60V~4zs4-!JptD1=p_i=xh7`YmjW`N`sD=Kj&qMa?QQ$o2eJt63VaS8hw0tv^RI*Wr^R3QzEXC<`z)BA@)!d1qU);f6X-dtaVzqmpB5pdYK3UbHD6BcPtZIAF4jlR^*v3EHm5S4x-QGv_9 zU(*1++ns!z4L-Z-lqhbmd2f7(Pfj!u_?Ei&+=a6Xj&w~&jtt%a*eLZ;&1n;pjCPY* zJL7;Cag{tbbQXX`@pLnAE7A^m%wCYqtR z4&B{hc&nrm!8&O3huQ}4=(ZdA%ah`E-+m(7>n0Z)%xf zB=#3>63;`8go0to5(;-fU_s4dPjH#{e3#@;z z=2~e|PH)3XJ%BRh=f9*oBerHJPtNL*Ip$Dgk5`q0^rASQEKH+bEOB_CNlNz}_H!P4 zJFGWWJm}~AjO?@_*^T}7wfQE>>yFHk$8q+(;6$_|TeL8aGE8?Mx8}Mdx42~OtaAPc zs{to#3Fv#y^1Kfcs~g7=g)K|RV}y5#qbWb0(`s7ch=bQ7R*jTZzhy-y`P?}|_d-dc z`qRoyguDh^*r-GRWnFFm2)@oT^R3z)?VNB9MXhL%{R_Mwkr-3@TH@{sz!0()N7P5vpyhhJiB{2{`P`nkvR>*fXaOaabGy?$5u;T5f z&YV#za|6#nDrQRGnjP6~zUKs*xd#0tFR2X<#bQ($z0zT^aYc0$>oO5{e$1fsgRk15 zu`@jnul_5@CMz6QU{@skaVQ=lFAD(c(AFP|osl$R1SIFBKQ{1xKXUgxKf_vb+tze8 zGG3=SAWp-Kq{K^vAhqY&JRaX;eG2h^TEnw$?OXWja)V*VCaXk{h>)y$+T6An^n?A# z`2C{>UP};O+34m-yPP0$bhzG5xd+?eOHcM643Fld9X>K#T0H}KzGKtKgU)N{&M)bG z&q_~6j)a*S(UB`r-rY^MO`-nuKmK2P7a99_O3wO}z@4hsL`sS7tt0}Lj+~J)37GA= z=mfuzJamQG*P-^G?y4WW6p*&4KV+m&dUtc}++x}7k9}6TQ5z}1CniDrS*Z0}3FF7Qc`9)kSfCf=k0gJ%z{hUKzdJq|dB z9=w*al^0ky(ah=lka2~RhOs!xeMRQRko!``$0Z+LT-S-Rb*w-j%wyj;RLd!lT-YsK zm~+c^aJ8Q4IDXiru)HnUb-Z3xNyf-IzD=t%m;X|$Qz6DMe}XNANs4oP7~|1b`vxJG zG*BXHubz^o7Sol>CFvSy>XUovpu-XI!lwtp8$!2u9Pr7E*&#JVZ z&HLa(zLLsM8<4b1z^4{x5EAMv1|W4|096iIyC7y-Ykn}JOCk`J>U2o)*Fo;T4;R1`_^g1Y`Qy2ApeI5_oNq(NG9H!q>Y@!1C<;iR=kHD+g@nUqW(gEoja3%j7RA;l;0^h|8 z5nTn&e4;p!+G?4{KgI^fw|s#*X$40FZADly^NOyM^69vQUu7G>g0pWS&cv49FoJll zYG)glih#>iftFdYYpKb!(p~DmU*th9B15qn8psqt7tYT6#a&7T=E9qxIu5(^wW|+~ zZ_baXyjl6_4Ery4_$0CkUI6~9R}$)Bb8A&*afye(^uk1mvzVA&fBZ#|u$*fIy8_&C z+6#cPlf8a%95$UTwy^>uCw@QXDQSgpy3^G*%H1eI=3SN_%Pus)!?yRGVbOw4pvlOp%gf#kS=`8?@5~vs?pWctZdJiLL;zr0fK$s-| zcaHA}=0Wk&)506CMeM2k@b_d){BT-6p_9_y;UHsQ_cq>L4cVF=O*?nF$5xVVXUC{c znDU9ZZf|xk@9Dp;8#JNj|AP&dOw_l_eE$sw{1~TK_vm)-B3Enaqpaqc!OE%YgAkn2 z_WvpEy8oKWvbc&4%&3T{AP6ETB2ED5O^9@rrZUn*7!e4a0qGE;(gZ<8L5vUrju1L1 zB_bs>MS7DOAoNg7fk+7@Z!ebJotd58ANCJ;pWNJc-#y>+J>Ty+;a@K_L>ANC?P}Hj z(aQzfm#DMe1X1|=Hs_*irbnJ3mgZR zHR9{OcLxJHIo`g`#JZC12WK@?F+xYAZYMgdT_w7LFZ%{8;>?WSkM!Fx5ob z6NHAJfZW^+V3HwY&h=iONHZFWp?xG6hj)OHAW*YtwQGcorGAMj7oOGhLWB;>h>3+a^u0V29?RO2ci1MWAnBT$`&2qx!OMko~0{}g8PIUyLKi-5Zx zB>mpz?1IOT_GUJmrLaws0XjYQ5Dad6D>3PBsxE=q98E{Iq z1QF6Ut}(&|6X1Rcg?!s1UbwEXyX~Pcx?#U9=FD#C>j!2nte@Ikho*_bao zLiy`&G=2xQ=Qtr*ht`aT>Z~w+d3TZ5wW_p_FF|@0?J=vl>Zb(`)IkKmyFSpc+hBCk zHkPrvc*-iT8C^(vQFY;9GNPA^NSEQi9w`ZR;TJl9^Z$zaoTTcv>>uh#2+W?%_t&jXj6xy*O0 zc4jwp4NvI^hvesX0Kj(WyM^#OX#16i6;Ml1-(0&-CzTKzKzfb&OG?cQhpIGZ9oG3ha%kFjaoRWhxG9GhVdNM5MZ*uniT)}3qJ$?Nfdb3Q~{FtKMQcT>rkeq z3fId5Fw$?o)@;oVC|{AS)$Z1(&K?|olY z^%!bp1PMwV+J}pyhFz%;{>Kr4i!}kn#xD=h*alrrM50(XPQ_fSt(#pi%kID*;F+A& zD%!m3IPswF;Oq5M;Z$W)*bg)%?K?Nt9Vr=tlX&4Up1ehz@_n<;$$|ThpI@T4xC{I@ zm*XEtl$6mzxinXQRdO1$oLd=|Tia+r2N3+sL<`19b=-DPJNp$7uPy_|Kg&L6C94W? zJP>axm<{4dT*>6dRIZnUq|tuXw^df{t}nPlS9)~UUsxhEMf^I3Cy%IkZTTqf^H7fW z>|m>{{QUUUi_J~x_3?7i>PhGHjvIASk&&nD<`pc-1aK5JXO_Pey}!+X*&5a22^U*I za%pl137GA4|9ydB*13DiIj()&zHsNPn$sXnar+O!8kq7BZ3)Zb7a;6M)7vfd+iV^* z@jWa`Nk-o{34_*eFY`B!VQuDDksA%^JJ1+p9LqAz)SCV<^RpJpU9B89@ZYT27-_1Q8#%TtQw zgB4*KYwYePvEj{*L#vH)VA94G#*}v&)T~(hzOnnppzYd0v6J-n1M2jchZ_m|N{52z zXIh`h6ECGaMH`mzmt;^$Myc3Ry^gmST=cN~nAiu8=KYr3#fdz*L|s`kSMHm?>$Y3z z)`_)ftxBc7^W2lyCQx92?gMGSRW0Q((}e%By#rOyd46?Y70Q6$&Zo!lv?1dh)FKXD zG~G>)gI}VJY-TO33q97W{6WN}k$GR143sRq(99cKv4>Nu^yP zq$xSyjT38LqkSLKZt~6z;|o4mdaw@j|VzYbpj2Ix-mThM`87@(xgjzp>7VajaO;zJBc**0>k# zavYmfdFxKcFwhu&9i#PaCea(zM2Hb}{a6^6x)LXrswWtT(${;aSHnF6vT4Vj=Sy-v zYp#Gs+==X}6w9-~YuJHLvymAidjz3aCWts22C;Kd{^+LuTzf2zH@c_eCfTt&O=*!NsFPFopsOoYO%B3I$hk$qW0Q(%^XlO2 z?)DfhN%)jmFA0fPXicJNNh2m)se0FZRZjL^a_BX(Sk5$qlW+^mrBj|O4My78^A$qC zjPka$R2`cu+o!9p7)0KYR^GgMaK&uM!xMV9HR)`qz!Q4EnLMy4G0#<|d|iXP0tO4EpS}Hj1ooaqi)uV=>X&>mz3@O|@$9cK zw|>40Ihy;rY$1vqQR?1FX0Go%-6MxpU2}YV*6-g6`o)-1gQM(N8&&%<;G-IE-BA`h z)Rt>7@-(rmB{wfmrbq<{_vR>=-C@-HXLPB8KTTWvcEoQq`t{8kI1@dTkjlY3bW&7y z>la?D^X+-oAm*O? zYzJ*m&44L5`%*r_7#qcF44LH*oZCxWej%mXo~HF<;>>U3Q?{NcR|SeX#t3MS9J}uv zwj^vG&f^%-Qiq3keHNXU0ICe-4v#&Y`zHnxWb6TQbyMEPFu7%q!DhG+7YxHUQZ|UH z5tTTK1NkP9I3Rl4Oe_D#8GY)y@4d~-6zeBhI(_YnA(3z`G2@0B?oS|AI#r@BS!Y;E zy*`A1Df_*;C^-@H-oh~7Y%(U24sCl;sp~KXX$C61JDJh{5jAmgu0QL3nw9iyFu%^! z=Iwm*$~$xTSq*ILbE5_m$CLNXJM>;_-Ocp~ftLxXSi#WRVt6TCHCQPp} zuPJOWRyWxqAKb(B3$Ow>x25Mh_c(i>9k;6@o?cW6I4aeyxNS?-A~qZ}GiZobF$7)p zeAZ%av=Az9#%x)75Tvx52!7QzV(~v;E=G+vXr7A)|M&6e@qFjYp^8!XowJJ}1tVp& z!#eLyRC9mIn(+sX$>FCl3OReEZY1zpnb;%UFmhhq5UW_PR!!`-GUL+kRvpoI+m*tUv`aam>FCxl~{lZ{dff5)Brtxwpp zB;bgIWqIf8F;pn(7^p{B#rTjdpt@Rojt^+ll#VCm+xek3b|xd5$b1au5jOuHkZ$F< z$@+4EE&dC8w+G*4e@Ql$EP!DMw7gb#uy^orpt$y^21zdW3d7vXu;Z-g zbqOlrjnD)uW?i02dV_tkuFe7MHQm+7{U0+;pb8R>kgCQma%#<$8&=~5F!9Qna5vKN z*9Q@&l@W)u+x(N4CR?nMMK(z6#eL^Fp_%|VS7QE#0lF;le*I8|jqNP}Otl(}Iy*YM zvjQ3Jp9eCtiY3Nj=^Md)q9?7ZG!R7v9gVxu-b#{aUrbC|2b-P~a@EL^hwGCm0Ad`i zlYe`KK84ji&@mzHf0B7|!br})WlwZWwEU!?`Rf=!Zc}51-t6~O@t=n2oW$b#a|JF|1!AsvjAZ26goJZxI@Yz6Lq7d6s?mgg`OQ+( z3EB(GDUwYrfy9!8j8s9x(Q_VrSe?~IRjzVIn#gv5%v!H`{UC46eToG zy3bs>$x=h3Y@Raq!Unud$6HR2GhaN~0zAxT@5E~%d_@df>nq;aP|Eako_| z@x>E-Vj?$ZM&mdJ%gD#VBPb`J>eflTvmg^@2`py+b>)EZzDKt)a4gsl2z{=%poj<% zzA&tDt`=T!&YTO;W__;Pp(Q=)38JHa;mORp(EG=IbedB~pO#}af(_^izr5zZU8$p} z&MFxSElgde%H9rKp3?)ELT6Zg4u}K&elMk{bgk1+=Rv-p3A(eqt^C`INE7(jHsC)! XQjTsoDDvZ$5b%PIk#@f3uXq0e)<1lD diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-clear.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointMarkerPlugin/images/breakpoint-clear.png index e031698377c6ec00774e3c0ffca07964f0c2ce07..85735d19c2c6673243f58715aec251608816aec3 100644 GIT binary patch delta 567 zcmV-70?7UC0+|JnZGQq5Nkl1wk0uj764Q(%?1GhlJ0=k&sz|x8n z{0B&a=?aq&C;tP%&9z1?%r4>rZ3jbA6Ig&nvE1qaa$CZ$Ye*12hjMFgVep-9&OOh2 z-t(SwglnX#?Z7b51t{77gl3-#3VbNAUmC?0Y2=H+Yytg`88|X z^&d^QE*okuF$v#H^Xr%U`*)@9{qMaBpnT?iZ1A zGk}P=s+t6HQ}b)q(dpTh(wvG%xUI#9lOj^CmRToNjcvQ$Yk>Fcj!0Qzo+ z7`fMZo-i8fc^8kG&wcocL7h>U>T|X)hf2V*h#WS~{R=#p`^GGu+^hfq002ovPDHLk FV1jVC7Wn`G delta 265 zcmV+k0rvix1?>WmZGQmjNkl@3C(7^~Uo%#TRuiz?T0>wcahbEvgDvo-_%YQD1b1#2h;V;>9%;T;Y z&Q_5fxp0jh^!y4O+5zY1Xj1+Jp1Z*dVeQ^#-C6W07s66y3)&g zbD^lG*p~T7M8f-w9D++cK6CiNr(fBT*iN!wSkhDJyjKx!sla{mZ8Be5MC%kz11=W5dU08j5;qoFL1&Db{XkwBBGS`DD4 z*B>+h+G`yr!u#q-)Oh{NDL{<@6wHU?-L6Rd`7~A9F90wXjsO%IfQp=k9F?K~NPK{M z2C!IcI|sm+xpt<(*y29`63n6j4Az(C?E$#@XU{Zn4S&o4P+yk23w$?3Wc_}1p~sS* zO3(NbFZ-rXgr9q-==BG&W~9+lUG!c=HUvObZJtqcvf1sksX{|p9v!s~ZWhEFHoCBe zYhZ?6e-IJqYPl71R@qBLWcBDOkD5adKmN9D#AVqg##ea?4&6e+Bzqye z_1ZlWXhZ6oSWcN;i2&fZWTEbQ?ruwU(T9SInU6(eb)QK(KvlI8KWBU5$pi*PWaD`4 Yf0@qou_T;JJ^%m!07*qoM6N<$f;I9S0ssI2 delta 194 zcmV;z06qV#1*Za#ZGQlyNkl;ZGQo-Nklh~YVKQv` zAAFM7xaw%yrT>9-WTk0?&RuF3e+`WZgD%*mwV#p>S}ubMBqS=w0Z>9etGwgQ`@Hwf zz2^u%Qq^7{0lWkh_ycT+$XUBL&;~}mVrAw1mz=wPaAJNH%6|ZiL_8%tlmwSpZ7a zE9TN!2iR8CQ4z5Qz$;c(7S{Iu3*R+`#npZ1$X1pCjH>pgzkBX|wf%Hay(GQsB~>+S zfW+6_iFq{L(0_XFv*wDR03**r zOuy>Co{tZGj`aoKHsOB)T7tsV*Lk3Elm|9N;M1&07*qoM6N<$ Ef@Y)M=l}o! delta 168 zcmV;Z09XI91dRfaZGQlYNklP?6h+|=m_U1Hz$tV96B6gJVF}|HCOUxy zJJ?cdYe?t`HWcI+gkEoXUha99f1)9HW|eTlrb7ktjB--YmG4*vr>Y^{^XS7k6m*}% zGAt5~A^Z^ZW)EdMNANA}96>SGj=0Sc}Tj<_VOcRa_|{c_$_ WVizA3Rq<^A0000 -

+

The breakpoints window tabulates and manipulates breakpoints among all live and traced + targets. Only address-based breakpoints are tabulated. For other traps, e.g., "break on + exception," see the Objects Window. Breakpoints + can also be manipulated from address-based views, especially the disassembly listings. See Breakpoints in + the Listings. Display of breakpoints in other views, e.g., the decompiler, is not yet + implemented.

+ +

Individual breakpoint locations from among the targets are consolidated into logical + breakpoints, based on their addresses in the static listing. The static locations are typically + stored as bookmarks in their respective Ghidra programs, comprising the current breakpoint set. + See the Static Mappings + window for the finer details of address mapping. A breakpoint which cannot be mapped to a + static address becomes its own logical breakpoint at its dynamic address. The top table of the + provider displays logical breakpoints; the bottom table displays breakpoint locations. + NOTE: The breakpoints window cannot display or manipulate breakpoints from a target + until that target is recorded into a trace. Furthermore, dead traces are not included. Nor are + breakpoints from the past included, even when viewing past machine state.

Depending on what is supported by the connected debugger, breakpoints can trap a target when an address or range is executed, read, or written; using software or hardware mechanisms. In @@ -54,75 +63,84 @@ mixed or inconsistent state. This happens quite commonly, e.g., when a breakpoint is placed in a Ghidra program before that program is mapped in any traced target. Once mapped in, the location of that breakpoint in the trace is computed and noted as missing. A logical breakpoint - without any location in a trace (i.e., on an actual target) is called "ineffective" and is - drawn in grey. An enabled logical breakpoint having a disabled location is called - "inconsistent" and its icon will indicate that state. A disabled logical breakpoint having an - enabled location is similarly "inconsistent." Toggling ineffective or inconsistent breakpoints - enables and/or places all locations, bringing it into a consistent enabled state. Toggling it - again disables all locations.

+ without any location in a trace (i.e., on an actual target) is called ineffective and + is drawn in grey, e.g.: . An enabled + logical breakpoint having a disabled location is called inconsistent and its icon will + include an exclamation mark: . A + disabled logical breakpoint having an enabled location is similarly inconsistent. Toggling an + ineffective or inconsistent logical breakpoint enables and/or places all its locations, aiming + for a consistent enabled state. Toggling it again disables all locations.

Tables and Columns

The top table, which lists logical breakpoints, has the following columns:

    -
  • Enabled - displays an icon indicating the state of the breakpoint. Clicking the icon - toggles the breakpoint.
  • +
  • State - displays an icon indicating the state of the breakpoint. If rendered in grey, the + breakpoint has no locations, i.e., it is ineffective. If rendered with an exclamation mark + overlay, the breakpoint is inconsistent. Clicking the icon toggles the breakpoint.
    • -
    • Enabled
    • +
    • Enabled: The logical breakpoint, + including all its locations, is enabled.
    • -
    • Disabled
    • +
    • Disabled: The logical breakpoint, + including all its locations, is disabled.
    • -
    • Inconsistent: Enabled with disabled - locations
    • - -
    • Inconsistent: Disabled with enabled - locations
    • - -
    • Enabled but ineffective
    • - -
    • Disabled and ineffective
    • +
    • Mixed: (Listing only) Two logical + breakpoints at the same address have different states.
  • Name - gives the user-defined name of the breakpoint. This cell is only populated and - modifiable when the breakpoint is mapped and saved to a program, since the name is associated - with the static location.
  • + modifiable when the breakpoint is bookmarked in a program, since the name is associated with + the static location. -
  • Address - gives the address of the breakpoint. For a breakpoint mapped to a program, this - gives the address in that program. For a breakpoint not mapped, this gives the address in its - trace.
  • +
  • Address - gives the address of the breakpoint. This is typically the static address. If + the breakpoint cannot be mapped to a static address, this is its dynamic address.
  • -
  • Image - gives the name of the program, if the breakpoint is mapped to one.
  • +
  • Image - gives the name of the static image, i.e., Ghidra program. If the breakpoint + cannot be mapped to a static location, this is blank.
  • -
  • Length - usually 1. For access breakpoints, this notes the length in bytes of the address - range.
  • +
  • Length - usually 1. For access breakpoints, this is the length of the address range (in + bytes).
  • Kinds - indicates the kind(s) of breakpoint: SW_EXECUTE, HW_EXECUTE, READ, and/or WRITE.
  • -
  • Locations - counts the number of trace locations mapped to this logical breakpoint, - applying the trace filter if active. Note that a logical breakpoint with 0 locations is +
  • Locations - counts the number of locations included in this logical breakpoint, applying + the trace filter if active. Note that a logical breakpoint with 0 locations is ineffective.
-

The bottom table, which lists trace breakpoint locations, has the following columns:

+

The bottom table, which lists breakpoint locations, has the following columns:

    -
  • Enabled - displays an icon indicating the state of the location. Clicking the icon - toggles the location.
  • +
  • State - displays an icon indicating the state of the location. If rendered with an + exclamation mark overlay, the location does not agree with its logical breakpoint, or it + cannot be bookmarked. Clicking the icon toggles the location.
  • + +
      +
    • Enabled: The location is + enabled.
    • + +
    • Disabled: The location is + disabled.
    • + +
    • Mixed: (Listing only) Two locations at + the same address have different states.
    • +
  • Name - displays the name given to the location by the connected debugger. This field is user modifiable.
  • -
  • Address - gives the address of this location in its trace.
  • +
  • Address - gives the dynamic address of this location.
  • -
  • Trace - gives the name of the location's trace — the target path by default.
  • +
  • Trace - gives the name of the location's trace.
  • -
  • Threads - if the breakpoint applies to a limited set of threads, gives the list of - threads.
  • +
  • Threads - (hidden by default) if the breakpoint applies to a limited set of threads, + gives the list of threads.
  • -
  • Comment - gives a user comment — the specification expression by default. This +
  • Comment - gives a user comment — the specification's expression by default. This field is user modifiable.
@@ -131,10 +149,10 @@

The primary purpose of this provider is to manipulate existing breakpoints. It provides the following actions to that end. Breakpoints can also be managed via the Breakpoint - Marker Actions in the disassembly listings.

+ Marker Actions in the listings.

-

Enable - Selected Breakpoints

+

+ Enable

This action is available when one or more breakpoints or locations are selected. It enables each selected breakpoint. For any breakpoint that is already enabled, no action is taken.

@@ -145,8 +163,8 @@

This action is always available. It enables every breakpoint. For any breakpoint that is already enabled, no action is taken.

-

Disable - Selected Breakpoints

+

+ Disable

This action is available when one or more breakpoints or locations are selected. It disables each selected breakpoint. For any breakpoint that is already disabled, no action is taken.

@@ -165,8 +183,7 @@ breakpoints where possible. This action is also offered as a resolution in the console. It appears in the log any time this action is available.

-

Clear - Selected Breakpoints

+

Clear

This action is available when one or more breakpoints or locations are selected. It clears (deletes) each selected breakpoint.

diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/DebuggerBreakpointsPlugin.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/DebuggerBreakpointsPlugin.png index 9eca75a21e1e8baacf4ef1ff05373e039a361b48..41ffdea30a8115f4b8e32b8c6134bf6e5c5db8a5 100644 GIT binary patch literal 27939 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%0DCEyIFUxyt8 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 literal 28254 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 literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-disable.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-disable.png index 6386998ebcc114a015717a8951a7fd780b86795f..4d3a03b13c8d817d401c11be961f7af62d259713 100644 GIT binary patch delta 586 zcmV-Q0=5080;~m)ZGQqONklfxp0jh^!y4O+5zY1Xj1+Jp1Z*dVeQ^#-C6W07s66y3)&g zbD^lG*p~T7M8f-w9D++cK6CiNr(fBT*iN!wSkhDJyjKx!sla{mZ8Be5MC%kz11=W5dU08j5;qoFL1&Db{XkwBBGS`DD4 z*B>+h+G`yr!u#q-)Oh{NDL{<@6wHU?-L6Rd`7~A9F90wXjsO%IfQp=k9F?K~NPK{M z2C!IcI|sm+xpt<(*y29`63n6j4Az(C?E$#@XU{Zn4S&o4P+yk23w$?3Wc_}1p~sS* zO3(NbFZ-rXgr9q-==BG&W~9+lUG!c=HUvObZJtqcvf1sksX{|p9v!s~ZWhEFHoCBe zYhZ?6e-IJqYPl71R@qBLWcBDOkD5adKmN9D#AVqg##ea?4&6e+Bzqye z_1ZlWXhZ6oSWcN;i2&fZWTEbQ?ruwU(T9SInU6(eb)QK(KvlI8KWBU5$pi*PWaD`4 Yf0@qou_T;JJ^%m!07*qoM6N<$f;I9S0ssI2 delta 194 zcmV;z06qV#1*Za#ZGQlyNkl$-i# za2y9~Z8@LM7b5cgPXkpw$+GM&48!qWv#0Ai!Z4fwx2k$7BBcSS>-sv1qJPTU#Bn@n zn&uL~sH&Hy>Df!Q_tCa3X_}s^>ahWqd7hhB+dX-n9|21Pyszu}P@k%*06rL=-}zuf z1Ym7|`{8hSaDeA|0QUw+eBWOm8ZezspMY;hM4BK7ZXCyXwLBOM2!i0Vh&-CU&V{wM z{GYhNTKnL-?kDJfqKK5Lx&UtDIG(g^`{K>k+K1U}wh)nDu(b;Ssyc3(<|<9o^P(t@ zc9!WW2!b!J>wetGe+6s>9rL`-@A{)3Kq4ZKyR*Lm9sr&h>jV?&00000NkvXXu0mjf D^0C|$ literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-enable.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-enable.png index ea1e9268110c56bf44d5a66528f777d7e405e2c8..2d618a99a741400e70a6c5da2cc51a2d381d70e2 100644 GIT binary patch delta 462 zcmV;<0WtoK0;ZGQo-Nklh~YVKQv` zAAFM7xaw%yrT>9-WTk0?&RuF3e+`WZgD%*mwV#p>S}ubMBqS=w0Z>9etGwgQ`@Hwf zz2^u%Qq^7{0lWkh_ycT+$XUBL&;~}mVrAw1mz=wPaAJNH%6|ZiL_8%tlmwSpZ7a zE9TN!2iR8CQ4z5Qz$;c(7S{Iu3*R+`#npZ1$X1pCjH>pgzkBX|wf%Hay(GQsB~>+S zfW+6_iFq{L(0_XFv*wDR03**r zOuy>Co{tZGj`aoKHsOB)T7tsV*Lk3Elm|9N;M1&07*qoM6N<$ Ef@Y)M=l}o! delta 168 zcmV;Z09XI91dRfaZGQlYNklP?6h+|=m_U1Hz$tV96B6gJVF}|HCOUxy zJJ?cdYe?t`HWcI+gkEoXUha99f1)9HW|eTlrb7ktjB--YmG4*vr>Y^{^XS7k6m*}% zGAt5~A^Z^ZW)EdMNANA}96>SGj=0Sc}Tj<_VOcRa_|{c_$_ WVizA3Rq<^A00004nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1U#L=PLN^!uU@$m4`C0EXW}5@iOSYPsAFE+_!Pu~BT~g2HO*-al*{4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1T6d%8G= zSoFT_Q{+43AmWmK;~nFQW$6zXzCPH=wd~}qt}vmzMt7yGDkdGNoM7KsXA=|}moBZS z`1JJoU&enM{H>k~Wxo|Wz{H>udhhodjjqcX2P~(%1eUM}?3bC8d%g7YCY|%=>x9_e z&3}BsdsbAo`NDc`o3!tWnLm9K1SU!GdaSb3vFKsj^Ih`fw&&H(MY2`GUAxxJHt{V# sHbb(3<$&mcc~7*?e?EO-SNt=!bRLtK9d{-z13H_*)78&qol`;+09f;Jj{pDw diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed-de.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed-de.png deleted file mode 100644 index 201d4a298d5ecc34411ce89b499e360ee0786b17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 368 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1Uvdb&7< zSoB`KxX|x#g2>U2@wYj7(wY>_nDrY{`44cLG|2cV%+(}}4wt#8Kg-p;s{M@7^Tg4jNgveDKi8Tb)Z0{dtl~(V z>;p+7HsxDOe=jh)d}?P@rPHCh6B5?hlMZ~DaW|f`%14QhJI^4V`Nk@1N1rOE{SVgV zSwGk)wjrRoc+dKZUmTWqIXXOxcd>eM&SK?J<`awmWB!FVyVhK3LY_+|(BllAu6{1- HoD!M4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1S(dAc}; zSoB`Kr0d5VD8usM{yQZD36{_q^B7#6RGYYSm_)yN@C7h3^&dB%qE_nA#K=C4`GOXY zib7V`i+9c&e9cZzi<0>M?ChEPd+h(^0;ldsFn-8W{o_ci-}bW^nGT6{hpJq^&${3x z8P5FZ)1nzMrq0TbQ!<+wh1Xo=H0*wHTxR<0Bcc~e8YRP+=JvEJo^@sXzMtjJ1FqgV zmv1%9(Yu#*M&9C-MazM_T!-0#hx`__M>@to&w2m3XUT&waaqSxKN|cVY+B%y5M}ex z(&f&%ul9DAWSnP7_TN~MvsXQ5>7FZQ8s;Z6S%2+&pDJXiG5bH;K897tZS`DN`^o}+ O&*16m=d#Wzp$Pz)7?1A& diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-mixed.png new file mode 100644 index 0000000000000000000000000000000000000000..f3a2d726e93b845651510c5b7a59b30c91e32c93 GIT binary patch literal 681 zcmV;a0#^NrP)?7UI}MeG zH>aZsf4Nz3MMSJEkEEF^{K3e`g(suV`1;>$!LaAq=sI`8YN~P9iAZ=mfvURv!N^GS z{h(77&Q=$5soq0P$pHX9Mt*T;aEku1A2fFdO<-76E4Gu&Sc#tfZO>F%d)3=Hth?3C zz!#O)UUjtf>1;Id03b(I3p-v<_6$u#1z;|=h5~1fxiUsLebi)hVVThPRqT1Ts&`NC z-8BI3i_oHxE^HqNbE7|;bq|;8JpcqkOF4ko07_=8M24_^Ak4MCv5fvncXBV&Rve(z z08}JH2f(dY6B!Ui0g&VaZW_RRxw$VpAq7bv=hOT$04dMB0Sq-(7H{rK;QyU;4o(8l zSY5OMyfs8*<$P_a-=1gN1CfMyJ4XnNFXG70rKPs)p@^&sfU27Q50P+lchFSblY^pR8s_elA_U<56GoE5{)17E(!^bJp7J?)S4z zFUOa1rhdi&@Eo$!P*t?iQd{<@Q3!H-^ccBWj3pC P00000NkvXXu0mjfWHl$9 literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-overlay-inconsistent.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoint-overlay-inconsistent.png new file mode 100644 index 0000000000000000000000000000000000000000..6eafda829e5bbf053342a9dab20c491be746d33e GIT binary patch literal 305 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1UndAc}; zSoCh4c$)W+fq=_=-%|^j1h_JrycR5#xy0h3ux{rPk^H2O-0V4vJG#w-x}3q&)z4*}Q$iB})e~<8 literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-clear-all.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-clear-all.png index 2988e9da8cc687d38f1bd2ef25f12bd0bf2bec30..a41a13c678245b34f5cf2534dc10f685b22289a5 100644 GIT binary patch delta 634 zcmV-=0)_qf1Kb6WZGQq;NklC)sD1snx5Jfj#EXw7LUG@hgb)+OqmIR7W_O;oha^FOqUkEi%7xP=w zdC_xo;GFY3@AI7ZJ%TMDr7QuC0@Z*7zQtm(p{lB?6TnNL9e)S_Yhn|zRMm8!e^l*w zHThlnJih{fyVOkH$bEUf>4muVFfV$?n{s&X(wo#dm!Czh4TPdWZ(rD!T>Z1b(Zu)| zawZ|>+~B{Fa)hTLsk04e5=P}fC_2^oIAn8JOaMd@`afskekQLz&xu-hhFkdns;2wA zePNr!Vq)l0Gk*ZBzF|5T1spY&dXQO{noChdnpZEPG*g zg8J5zgs-=qQUK?d_)4aFB%#yXGmNU~83(fqzi7Mn7Jsdx0Se9FKEjN)0#Ks>Qfz4E z|3M%C))XMJ)4pRv{@~>nT()$CNJ6LDvYj^<)3FS!b0P{b*j`t$wpIRCUog{+>@$G& z{pBfOND)FTb~V-n%!Qem%8(5Q*)0VqE-Jv&=z1cASTY959(W&}>hy=~TMd%o4j{Uq z)9Y!N?{hfhs1-tJ`2a$QxRkO5818=d!M?ouJLl0N37z7i0)yNlgwWEDnFT4O?Q2}` z@xN2gzMlM%_vvR60C$<0kHBfV(3y3q<@7plhDN?2nvEj zopf-qw40lQs~sX36hs9}HSlz3!AM1tshf-7*s&QJ#Gi%Ip}ylvCqXQ^s-DK86khdG=Jo1VH=aYJLEEs$^<5& z&t@cdzB7B1jcqjWoP%-XzsUHN5uP#|t%gZDZ9>M+D(=K^$g#`=EaJ^7OKGtR{}ssT zwQ#_dDLN0iGs7cDcwfRxv4-mj(o<~gbE$!qxQ+N4xRLM}XQG|GC3e1W95IxXnGWo%`HP9amE+5ZG(s8w+*1PiUSlGJ!rZoH0me_%L9;q@9p5 zca@J-4%PSWo53QL#LWX^Cd_M-wN^#O{b&PO3ku480369s8Ms` z_xSL9p7%!)MIw?6lmPnx0Y3SBzOMB2^m4!jlmSj)MMZ!H&wqUImZQgGZ5#G$A7_^U zFdGtyd3{Ifik_=0%`x5vTi9f~NoycTW$Zt2wzJp!%Jz6r7YHr?SJC_ax5(so_1O%L zt6|gN7zBcB1B%poW@oQ=r0RaJP6R6QjnrGr95E#WU^{=A3&l+vTFp7dH2|jOmRfh7 zeJZNviQITGCV!D~yWLV+S}KjT$jfG=u@=eAKj;H2nusJjdOX%ZXqk$9BbQDb*pSI$ zv9Q;u!)SmS`yPx2h)*%5iAcW&l(>fc8bH0p9BnB8+`abzF|}~p*-cfKjc`$&0!m_m zjH$WRX&+weIhr(0qq4G+Kxi4v#Z1mG0c2``D77iKL4Se(pilrS8Zfh8zXN~)@4}V= z24;Q$u*S@2KxbJ_+6sW(J+Wl~d&f8cWqGM#pi5Iq{XA2c=}e4|<=KZXG_;Rx3SVy< z#Wmz7DIt#P!mJjh)Z+Rf>79Mvkt#>8UIfbX(l}=^b2MWY0DaR7*gM8?4f#+vm^W^XD8tRm00kU(@1H58}Qxp9@Zjge{z^`GW;&G z{DYNku77}0wfX~Hz%$kZrL4cHIrLI$MMJDWU^C^mPB&vKrdVLIE&Qs%YNp0LiUXU^ zI6G$Jol=R@h&g+O204v{^PIVin=pg|^}-fX+8?Z-myjp@nI^A)#6YC46RY*KV S_+icf0000XR$;MJ&Xm%6RhX%ZaKh%awdeHl z@co|m`964s7E(%e0DfQ}pn%W0TrTc#I8FjHKmdpVHE9B^nSWwsGMdN)pQKl`5BV|x zZmS91i`(kr&||q8(PeLjtIqHQ_e&Md={LX!W6A9E@KnlDTKlu9(Zc*I)Z%wpc0=@9 z)tPz+r3$-&A!$sGC9`iscaj#RT0ANBko5SYExzY8*EN7lu`(H+O0|Rm#C8DBr9({5 z!IuI|&%v1+-hX8vp(&+0qKQnfx%MX6`!SeJ*sU-+Va09*L$|$0DK)PL{4;M>wDxpe zzmA3p#RuS+mr#5G@9|Rt`1OG6W1+kq4ucxTUlUML{^*c{sn;Aw6*P6 S{B{5U00{s|MNUMnLSTZXfdIV# delta 228 zcmVA6?4H2%sV(&s2GR-(IY&Uegya~) zYM-RAPMg*`X+^M?O_52nI3JLwnVI+F|IW*z|KSahexg_MRGK+&BNV2$&Q_^IK!2d-f diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-make-effective.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-make-effective.png index b1e1ab4ede499f1cfbdafd425a95c7924f5cffe9..d220d56af110fe6bccf99ab417192f99b205bbd5 100644 GIT binary patch delta 302 zcmV+}0nz^b0+IueZGQm|Nkl{wevsSZl||m`eZ? z03ew==i($u3NOG~J66>f0Kp$qq^zncOw)AT1{eT9Gdb8bNq>%vFkwD7wV*@Id5)>;jX5s=$)nF8=iDJ4;W zh;)GtwjqE9aL!d)>jx2;UAUj}{k~np3*TdQKvhBy*D { + provider.breakpointService.enableLocs(Set.of(loc)).exceptionally(ex -> { provider.breakpointError("Toggle breakpoint", "Could not enable breakpoint", ex); return null; }); } else { - bpt.getSpecification().disable().exceptionally(ex -> { + provider.breakpointService.disableLocs(Set.of(loc)).exceptionally(ex -> { provider.breakpointError("Toggle breakpoint", "Could not disable breakpoint", ex); return null; }); } } + public void setState(State state) { + assert state.isNormal(); + setEnabled(state.isEnabled()); + } + public void setName(String name) { try (UndoableTransaction tid = UndoableTransaction.start(loc.getTrace(), "Set breakpoint name", true)) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointEnablementTableCellRenderer.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointEnablementTableCellRenderer.java deleted file mode 100644 index a8a09b7050..0000000000 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointEnablementTableCellRenderer.java +++ /dev/null @@ -1,72 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - */ -package ghidra.app.plugin.core.debug.gui.breakpoint; - -import java.awt.Component; - -import javax.swing.Icon; -import javax.swing.SwingConstants; - -import docking.widgets.table.GTableCellRenderingData; -import ghidra.app.plugin.core.debug.gui.DebuggerResources; -import ghidra.app.services.LogicalBreakpoint.Enablement; -import ghidra.docking.settings.Settings; -import ghidra.util.table.column.AbstractGColumnRenderer; - -public class DebuggerBreakpointEnablementTableCellRenderer - extends AbstractGColumnRenderer { - - protected static Icon iconForEnablement(Enablement en) { - switch (en) { - case NONE: - return null; - case ENABLED: - return DebuggerResources.ICON_BREAKPOINT_ENABLED_MARKER; - case DISABLED: - return DebuggerResources.ICON_BREAKPOINT_DISABLED_MARKER; - case INEFFECTIVE_ENABLED: - return DebuggerResources.ICON_BREAKPOINT_INEFFECTIVE_E_MARKER; - case INEFFECTIVE_DISABLED: - return DebuggerResources.ICON_BREAKPOINT_INEFFECTIVE_D_MARKER; - case ENABLED_DISABLED: - return DebuggerResources.ICON_BREAKPOINT_MIXED_ED_MARKER; - case DISABLED_ENABLED: - return DebuggerResources.ICON_BREAKPOINT_MIXED_DE_MARKER; - default: - throw new AssertionError(en); - } - } - - public DebuggerBreakpointEnablementTableCellRenderer() { - setHorizontalAlignment(SwingConstants.CENTER); - } - - @Override - public Component getTableCellRendererComponent(GTableCellRenderingData data) { - super.getTableCellRendererComponent(data); - Enablement en = (Enablement) data.getValue(); - setIcon(iconForEnablement(en)); - setHorizontalAlignment(SwingConstants.CENTER); - setText(""); - setToolTipText(en.name()); - return this; - } - - @Override - public String getFilterString(Enablement t, Settings settings) { - return t.name(); - } -} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocEnabledTableCellEditor.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocEnabledTableCellEditor.java deleted file mode 100644 index 30f3651230..0000000000 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocEnabledTableCellEditor.java +++ /dev/null @@ -1,67 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - */ -package ghidra.app.plugin.core.debug.gui.breakpoint; - -import java.awt.Component; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.*; -import javax.swing.plaf.basic.BasicButtonUI; -import javax.swing.table.TableCellEditor; - -public class DebuggerBreakpointLocEnabledTableCellEditor extends AbstractCellEditor - implements TableCellEditor, ActionListener { - protected final JButton button = new JButton(); - - private Boolean value = false; - - public DebuggerBreakpointLocEnabledTableCellEditor() { - button.setHorizontalAlignment(SwingConstants.CENTER); - button.setOpaque(true); - button.setBorder(BorderFactory.createEmptyBorder()); - button.setUI(new BasicButtonUI()); - - button.addActionListener(this); - } - - @Override - public Object getCellEditorValue() { - return value; - } - - @Override - public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, - int row, int column) { - if (isSelected) { - button.setBackground(table.getSelectionBackground()); - } - else { - // TODO: Alternating colors? Can't inherit GTableCellRenderer.... - button.setBackground(table.getBackground()); - } - this.value = (Boolean) value; - button.setIcon(DebuggerBreakpointLocEnabledTableCellRenderer.iconForEnabled(this.value)); - button.setHorizontalAlignment(SwingConstants.CENTER); - return button; - } - - @Override - public void actionPerformed(ActionEvent e) { - value = value == null ? true : !value; - fireEditingStopped(); - } -} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocationsActionContext.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocationsActionContext.java index 75b7bcf84d..a57df3770c 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocationsActionContext.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocationsActionContext.java @@ -16,18 +16,23 @@ package ghidra.app.plugin.core.debug.gui.breakpoint; import java.util.Collection; +import java.util.stream.Collectors; import docking.ActionContext; import ghidra.trace.model.breakpoint.TraceBreakpoint; public class DebuggerBreakpointLocationsActionContext extends ActionContext { - private final Collection selection; + private final Collection selection; - public DebuggerBreakpointLocationsActionContext(Collection selection) { + public DebuggerBreakpointLocationsActionContext(Collection selection) { this.selection = selection; } - public Collection getSelection() { + public Collection getSelection() { return selection; } + + public Collection getLocations() { + return selection.stream().map(row -> row.getTraceBreakpoint()).collect(Collectors.toList()); + } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPlugin.java index bf24ac7145..f706b909d9 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPlugin.java @@ -36,7 +36,7 @@ import ghidra.app.plugin.core.debug.event.TraceOpenedPluginEvent; import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.app.plugin.core.debug.gui.DebuggerResources.*; import ghidra.app.services.*; -import ghidra.app.services.LogicalBreakpoint.Enablement; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.app.util.viewer.listingpanel.MarkerClickedListener; import ghidra.async.AsyncDebouncer; import ghidra.async.AsyncTimer; @@ -216,18 +216,51 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin return result; } + protected Color colorForState(State state) { + if (state.isEnabled()) { + if (state.isEffective()) { + return breakpointEnabledMarkerColor; + } + else { + return breakpointIneffEnMarkerColor; + } + } + else { + if (state.isEffective()) { + return breakpointDisabledMarkerColor; + } + else { + return breakpointIneffDisMarkerColor; + } + } + } + + protected boolean stateColorsBackground(State state) { + if (state.isEnabled()) { + if (state.isEffective()) { + return breakpointEnabledColoringBackground; + } + else { + return breakpointIneffEnColoringBackground; + } + } + else { + if (state.isEffective()) { + return breakpointDisabledColoringBackground; + } + else { + return breakpointIneffDisColoringBackground; + } + } + } + /** * A variety of marker sets (one for each logical state) attached to a program or trace view */ protected class BreakpointMarkerSets { final Program program; - final MarkerSet enabled; - final MarkerSet disabled; - final MarkerSet ineffectiveE; - final MarkerSet ineffectiveD; - final MarkerSet mixedED; - final MarkerSet mixedDE; + final Map sets = new HashMap<>(); protected BreakpointMarkerSets(Program program) { this.program = program; @@ -238,227 +271,122 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin manager.defineType(LogicalBreakpoint.BREAKPOINT_ENABLED_BOOKMARK_TYPE, DebuggerResources.ICON_BLANK, DebuggerResources.DEFAULT_COLOR_ENABLED_BREAKPOINT_MARKERS, - DebuggerResources.PRIORITY_BREAKPOINT_ENABLED_MARKER - 1); + MarkerService.BREAKPOINT_PRIORITY - 1); manager.defineType(LogicalBreakpoint.BREAKPOINT_DISABLED_BOOKMARK_TYPE, DebuggerResources.ICON_BLANK, DebuggerResources.DEFAULT_COLOR_ENABLED_BREAKPOINT_MARKERS, - DebuggerResources.PRIORITY_BREAKPOINT_DISABLED_MARKER - 1); + MarkerService.BREAKPOINT_PRIORITY - 1); } - enabled = getEnabledMarkerSet(); - disabled = getDisabledMarkerSet(); - ineffectiveE = getIneffectiveEMarkerSet(); - ineffectiveD = getIneffectiveDMarkerSet(); - mixedED = getMixedEDMarkerSet(); - mixedDE = getMixedDEMarkerSet(); + for (State state : State.values()) { + getMarkerSet(state); + } } - private MarkerSet getEnabledMarkerSet() { - MarkerSet set = markerService - .getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_ENABLED, program); + MarkerSet getMarkerSet(State state) { + return sets.computeIfAbsent(state, this::doGetMarkerSet); + } + + MarkerSet doGetMarkerSet(State state) { + if (state.icon == null) { + return null; + } + MarkerSet set = markerService.getMarkerSet(state.display, program); if (set != null) { return set; } - return markerService.createPointMarker( - DebuggerResources.MARKER_NAME_BREAKPOINT_ENABLED, - DebuggerResources.MARKER_NAME_BREAKPOINT_ENABLED, program, - DebuggerResources.PRIORITY_BREAKPOINT_ENABLED_MARKER, true, true, true, - breakpointEnabledMarkerColor, DebuggerResources.ICON_BREAKPOINT_ENABLED_MARKER, - true); - } - - private MarkerSet getDisabledMarkerSet() { - MarkerSet set = markerService - .getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_DISABLED, program); - if (set != null) { - return set; - } - return markerService.createPointMarker( - DebuggerResources.MARKER_NAME_BREAKPOINT_DISABLED, - DebuggerResources.MARKER_NAME_BREAKPOINT_DISABLED, program, - DebuggerResources.PRIORITY_BREAKPOINT_DISABLED_MARKER, true, false, false, - breakpointEnabledMarkerColor, DebuggerResources.ICON_BREAKPOINT_DISABLED_MARKER, - false); - } - - private MarkerSet getIneffectiveEMarkerSet() { - MarkerSet set = markerService - .getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_INEFFECTIVE_E, program); - if (set != null) { - return set; - } - return markerService.createPointMarker( - DebuggerResources.MARKER_NAME_BREAKPOINT_INEFFECTIVE_E, - DebuggerResources.MARKER_NAME_BREAKPOINT_INEFFECTIVE_E, program, - DebuggerResources.PRIORITY_BREAKPOINT_INEFFECTIVE_E_MARKER, true, false, true, - breakpointIneffectiveEMarkerColor, - DebuggerResources.ICON_BREAKPOINT_INEFFECTIVE_E_MARKER, - false); - } - - private MarkerSet getIneffectiveDMarkerSet() { - MarkerSet set = markerService - .getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_INEFFECTIVE_D, program); - if (set != null) { - return set; - } - return markerService.createPointMarker( - DebuggerResources.MARKER_NAME_BREAKPOINT_INEFFECTIVE_D, - DebuggerResources.MARKER_NAME_BREAKPOINT_INEFFECTIVE_D, program, - DebuggerResources.PRIORITY_BREAKPOINT_INEFFECTIVE_D_MARKER, true, false, false, - breakpointIneffectiveDMarkerColor, - DebuggerResources.ICON_BREAKPOINT_INEFFECTIVE_D_MARKER, - false); - } - - private MarkerSet getMixedEDMarkerSet() { - MarkerSet set = markerService - .getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_MIXED_ED, program); - if (set != null) { - return set; - } - return markerService.createPointMarker( - DebuggerResources.MARKER_NAME_BREAKPOINT_MIXED_ED, - DebuggerResources.MARKER_NAME_BREAKPOINT_MIXED_ED, program, - DebuggerResources.PRIORITY_BREAKPOINT_MIXED_ED_MARKER, true, true, true, - breakpointEnabledMarkerColor, DebuggerResources.ICON_BREAKPOINT_MIXED_ED_MARKER, - false); - } - - private MarkerSet getMixedDEMarkerSet() { - MarkerSet set = markerService - .getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_MIXED_DE, program); - if (set != null) { - return set; - } - return markerService.createPointMarker( - DebuggerResources.MARKER_NAME_BREAKPOINT_MIXED_DE, - DebuggerResources.MARKER_NAME_BREAKPOINT_MIXED_DE, program, - DebuggerResources.PRIORITY_BREAKPOINT_MIXED_DE_MARKER, true, false, false, - breakpointEnabledMarkerColor, DebuggerResources.ICON_BREAKPOINT_MIXED_DE_MARKER, - false); - } - - MarkerSet get(Enablement en) { - switch (en) { - case ENABLED: - return enabled; - case DISABLED: - return disabled; - case INEFFECTIVE_ENABLED: - return ineffectiveE; - case INEFFECTIVE_DISABLED: - return ineffectiveD; - case ENABLED_DISABLED: - return mixedED; - case DISABLED_ENABLED: - return mixedDE; - case NONE: - return null; - default: - throw new AssertionError(); - } + return markerService.createPointMarker(state.display, state.display, program, + MarkerService.BREAKPOINT_PRIORITY, true, true, stateColorsBackground(state), + colorForState(state), state.icon, true); } public void setEnabledMarkerColor(Color color) { - if (enabled != null) { - enabled.setMarkerColor(color); - } - if (mixedED != null) { - mixedED.setMarkerColor(color); + for (State state : State.values()) { + if (state == State.NONE || !state.isEnabled() || !state.isEffective()) { + continue; + } + getMarkerSet(state).setMarkerColor(color); } } public void setDisabledMarkerColor(Color color) { - if (disabled != null) { - disabled.setMarkerColor(color); - } - if (mixedDE != null) { - mixedDE.setMarkerColor(color); + for (State state : State.values()) { + if (state == State.NONE || state.isEnabled() || !state.isEffective()) { + continue; + } + getMarkerSet(state).setMarkerColor(color); } } public void setIneffectiveEnabledMarkerColor(Color color) { - if (ineffectiveE != null) { - ineffectiveE.setMarkerColor(color); + for (State state : State.values()) { + if (state == State.NONE || !state.isEnabled() || state.isEffective()) { + continue; + } + getMarkerSet(state).setMarkerColor(color); } } public void setIneffectiveDisabledMarkerColor(Color color) { - if (ineffectiveD != null) { - ineffectiveD.setMarkerColor(color); + for (State state : State.values()) { + if (state == State.NONE || state.isEnabled() || state.isEffective()) { + continue; + } + getMarkerSet(state).setMarkerColor(color); } } public void setEnabledColoringBackground(boolean coloringBackground) { - if (enabled != null) { - enabled.setColoringBackground(coloringBackground); - } - if (mixedED != null) { - mixedED.setColoringBackground(coloringBackground); + for (State state : State.values()) { + if (state == State.NONE || !state.isEnabled() || !state.isEffective()) { + continue; + } + getMarkerSet(state).setColoringBackground(coloringBackground); } } public void setDisabledColoringBackground(boolean coloringBackground) { - if (disabled != null) { - disabled.setColoringBackground(coloringBackground); - } - if (mixedDE != null) { - mixedDE.setColoringBackground(coloringBackground); + for (State state : State.values()) { + if (state == State.NONE || state.isEnabled() || !state.isEffective()) { + continue; + } + getMarkerSet(state).setColoringBackground(coloringBackground); } } public void setIneffectiveEnabledColoringBackground(boolean coloringBackground) { - if (ineffectiveE != null) { - ineffectiveE.setColoringBackground(coloringBackground); + for (State state : State.values()) { + if (state == State.NONE || !state.isEnabled() || state.isEffective()) { + continue; + } + getMarkerSet(state).setColoringBackground(coloringBackground); } } public void setIneffectiveDisabledColoringBackground(boolean coloringBackground) { - if (ineffectiveD != null) { - ineffectiveD.setColoringBackground(coloringBackground); + for (State state : State.values()) { + if (state == State.NONE || state.isEnabled() || state.isEffective()) { + continue; + } + getMarkerSet(state).setColoringBackground(coloringBackground); } } public void dispose() { - if (enabled != null) { - markerService.removeMarker(enabled, program); - } - if (disabled != null) { - markerService.removeMarker(disabled, program); - } - if (ineffectiveE != null) { - markerService.removeMarker(ineffectiveE, program); - } - if (ineffectiveD != null) { - markerService.removeMarker(ineffectiveD, program); - } - if (mixedED != null) { - markerService.removeMarker(mixedED, program); - } - if (mixedDE != null) { - markerService.removeMarker(mixedDE, program); + for (State state : State.values()) { + MarkerSet set = sets.get(state); + if (set != null) { + markerService.removeMarker(set, program); + } } } public void clear() { - if (enabled != null) { - enabled.clearAll(); - } - if (disabled != null) { - disabled.clearAll(); - } - if (ineffectiveE != null) { - ineffectiveE.clearAll(); - } - if (ineffectiveD != null) { - ineffectiveD.clearAll(); - } - if (mixedED != null) { - mixedED.clearAll(); - } - if (mixedDE != null) { - mixedDE.clearAll(); + for (State state : State.values()) { + MarkerSet set = sets.get(state); + if (set != null) { + set.clearAll(); + } } } } @@ -493,35 +421,27 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin } } - protected static Enablement computeEnablement(LogicalBreakpoint breakpoint, - Program programOrView) { + protected static State computeState(LogicalBreakpoint breakpoint, Program programOrView) { if (programOrView instanceof TraceProgramView) { TraceProgramView view = (TraceProgramView) programOrView; - return breakpoint.computeEnablementForTrace(view.getTrace()); + return breakpoint.computeStateForTrace(view.getTrace()); } // Program view should consider all trace placements // TODO: A mode for only considering the current trace (for effectiveness in program) - return breakpoint.computeEnablement(); + return breakpoint.computeState(); } /** - * TODO: Document me - * - *

- * This is a little different from that in the breakpoint service. + * It seems the purpose of this was to omit the program mode from the dynamic listing. I don't + * think we need that anymore, so I've just delegated to exactly the same as the breakpoint + * service, which will include the program mode, if applicable. TODO: Remove this and just call + * the service's version directly? * * @param loc * @return */ - protected Enablement computeEnablement(ProgramLocation loc) { - Program programOrView = loc.getProgram(); - if (programOrView instanceof TraceProgramView) { - return breakpointService.computeEnablement(loc).getPrimary(); - } - // Program view should consider all trace breakpoints, too - // breakpointService.computeEnablement(loc) only considers program breakpoint - Set bs = breakpointService.getBreakpointsAt(loc); - return breakpointService.computeEnablement(bs); + protected State computeState(ProgramLocation loc) { + return breakpointService.computeState(loc); } protected class ToggleBreakpointAction extends AbstractToggleBreakpointAction { @@ -623,8 +543,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin return false; } ProgramLocation location = getLocationFromContext(context); - Enablement en = computeEnablement(location); - if (en == Enablement.ENABLED || en == Enablement.NONE) { + State state = computeState(location); + if (state == State.ENABLED || state == State.NONE) { return false; } return true; @@ -660,8 +580,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin return false; } ProgramLocation location = getLocationFromContext(context); - Enablement en = computeEnablement(location); - if (en == Enablement.DISABLED || en == Enablement.NONE) { + State state = computeState(location); + if (state == State.DISABLED || state == State.NONE) { return false; } return true; @@ -699,8 +619,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin return false; } ProgramLocation location = getLocationFromContext(context); - Enablement en = computeEnablement(location); - if (en == Enablement.NONE) { + State state = computeState(location); + if (state == State.NONE) { return false; } return true; @@ -751,32 +671,32 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin DebuggerResources.DEFAULT_COLOR_DISABLED_BREAKPOINT_COLORING_BACKGROUND; @AutoOptionDefined( - name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_E_BREAKPOINT_MARKERS, // + name = DebuggerResources.OPTION_NAME_COLORS_INEFF_EN_BREAKPOINT_MARKERS, // description = "Background color for memory at an enabled, but ineffective, breakpoint", // help = @HelpInfo(anchor = "colors")) - private Color breakpointIneffectiveEMarkerColor = - DebuggerResources.DEFAULT_COLOR_INEFFECTIVE_E_BREAKPOINT_MARKERS; + private Color breakpointIneffEnMarkerColor = + DebuggerResources.DEFAULT_COLOR_INEFF_EN_BREAKPOINT_MARKERS; @AutoOptionDefined( - name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_E_BREAKPOINT_COLORING_BACKGROUND, // + name = DebuggerResources.OPTION_NAME_COLORS_INEFF_EN_BREAKPOINT_COLORING_BACKGROUND, // description = "Whether or not to color background for memory at an enabled, but ineffective, breakpoint", // help = @HelpInfo(anchor = "colors")) - private boolean breakpointIneffectiveEColoringBackground = - DebuggerResources.DEFAULT_COLOR_INEFFECTIVE_E_BREAKPOINT_COLORING_BACKGROUND; + private boolean breakpointIneffEnColoringBackground = + DebuggerResources.DEFAULT_COLOR_INEFF_EN_BREAKPOINT_COLORING_BACKGROUND; @AutoOptionDefined( - name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_D_BREAKPOINT_MARKERS, // + name = DebuggerResources.OPTION_NAME_COLORS_INEFF_DIS_BREAKPOINT_MARKERS, // description = "Background color for memory at an disabled, but ineffective, breakpoint", // help = @HelpInfo(anchor = "colors")) - private Color breakpointIneffectiveDMarkerColor = - DebuggerResources.DEFAULT_COLOR_INEFFECTIVE_D_BREAKPOINT_MARKERS; + private Color breakpointIneffDisMarkerColor = + DebuggerResources.DEFAULT_COLOR_INEFF_DIS_BREAKPOINT_MARKERS; @AutoOptionDefined( - name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_D_BREAKPOINT_COLORING_BACKGROUND, // + name = DebuggerResources.OPTION_NAME_COLORS_INEFF_DIS_BREAKPOINT_COLORING_BACKGROUND, // description = "Whether or not to color background for memory at an disabled, but ineffective, breakpoint", // help = @HelpInfo(anchor = "colors")) - private boolean breakpointIneffectiveDColoringBackground = - DebuggerResources.DEFAULT_COLOR_INEFFECTIVE_D_BREAKPOINT_COLORING_BACKGROUND; + private boolean breakpointIneffDisColoringBackground = + DebuggerResources.DEFAULT_COLOR_INEFF_DIS_BREAKPOINT_COLORING_BACKGROUND; @SuppressWarnings("unused") private final AutoOptions.Wiring autoOptionsWiring; @@ -789,7 +709,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin new ToggleBreakpointsMarkerClickedListener(); private final AsyncDebouncer updateDebouncer = - new AsyncDebouncer<>(AsyncTimer.DEFAULT_TIMER, 100); + new AsyncDebouncer<>(AsyncTimer.DEFAULT_TIMER, 50); // package access for testing SetBreakpointAction actionSetSoftwareBreakpoint; @@ -851,7 +771,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin } @AutoOptionConsumed( - name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_E_BREAKPOINT_MARKERS) + name = DebuggerResources.OPTION_NAME_COLORS_INEFF_EN_BREAKPOINT_MARKERS) private void setIneffectiveEBreakpointMarkerColor(Color breakpointMarkerColor) { for (BreakpointMarkerSets markers : markersByProgram.values()) { markers.setIneffectiveEnabledMarkerColor(breakpointMarkerColor); @@ -859,7 +779,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin } @AutoOptionConsumed( - name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_E_BREAKPOINT_COLORING_BACKGROUND) + name = DebuggerResources.OPTION_NAME_COLORS_INEFF_EN_BREAKPOINT_COLORING_BACKGROUND) private void setIneffectiveEBreakpointMarkerBackground(boolean breakpointColoringBackground) { for (BreakpointMarkerSets markers : markersByProgram.values()) { markers.setIneffectiveEnabledColoringBackground(breakpointColoringBackground); @@ -867,7 +787,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin } @AutoOptionConsumed( - name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_D_BREAKPOINT_MARKERS) + name = DebuggerResources.OPTION_NAME_COLORS_INEFF_DIS_BREAKPOINT_MARKERS) private void setIneffectiveDBreakpointMarkerColor(Color breakpointMarkerColor) { for (BreakpointMarkerSets markers : markersByProgram.values()) { markers.setIneffectiveDisabledMarkerColor(breakpointMarkerColor); @@ -875,7 +795,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin } @AutoOptionConsumed( - name = DebuggerResources.OPTION_NAME_COLORS_INEFFECTIVE_D_BREAKPOINT_COLORING_BACKGROUND) + name = DebuggerResources.OPTION_NAME_COLORS_INEFF_DIS_BREAKPOINT_COLORING_BACKGROUND) private void setIneffectiveDBreakpointMarkerBackground(boolean breakpointColoringBackground) { for (BreakpointMarkerSets markers : markersByProgram.values()) { markers.setIneffectiveDisabledColoringBackground(breakpointColoringBackground); @@ -961,7 +881,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin placeBreakpointDialog.prompt(tool, breakpointService, title, loc, length, kinds, ""); return; } - Enablement en = breakpointService.computeEnablement(bs, loc); + State state = breakpointService.computeState(bs, loc); /** * If we're in the static listing, this will return null, indicating we should use the * program's perspective. The methods taking trace should accept a null trace and behave @@ -969,8 +889,8 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin */ Trace trace = getTraceFromContext(context); boolean mapped = breakpointService.anyMapped(bs, trace); - Enablement toggled = en.getToggled(mapped && trace == null); - if (toggled.enabled) { + State toggled = state.getToggled(mapped); + if (toggled.isEnabled()) { breakpointService.enableAll(bs, trace).exceptionally(ex -> { breakpointError(title, "Could not enable breakpoints", ex); return null; @@ -1008,17 +928,17 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin protected void doMarks(BreakpointMarkerSets marks, Map> byAddress, - java.util.function.Function enFunc) { + java.util.function.Function stateFunc) { for (Map.Entry> bEnt : byAddress.entrySet()) { - Map en = new HashMap<>(); + Map byLength = new HashMap<>(); for (LogicalBreakpoint lb : bEnt.getValue()) { - en.compute(lb.getLength(), (l, e) -> (e == null ? Enablement.NONE : e) - .sameAdddress(enFunc.apply(lb))); + byLength.compute(lb.getLength(), (l, e) -> (e == null ? State.NONE : e) + .sameAdddress(stateFunc.apply(lb))); } Address start = bEnt.getKey(); - for (Map.Entry eEnt : en.entrySet()) { - Address end = start.add(eEnt.getKey() - 1); - MarkerSet set = marks.get(eEnt.getValue()); + for (Map.Entry sEnt : byLength.entrySet()) { + Address end = start.add(sEnt.getKey() - 1); + MarkerSet set = marks.getMarkerSet(sEnt.getValue()); if (set != null) { set.add(start, end); } @@ -1041,11 +961,11 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin TraceProgramView view = (TraceProgramView) program; Trace trace = view.getTrace(); doMarks(marks, breakpointService.getBreakpoints(trace), - lb -> lb.computeEnablementForTrace(trace)); + lb -> lb.computeStateForTrace(trace)); } else { doMarks(marks, breakpointService.getBreakpoints(program), - lb -> lb.computeEnablementForProgram(program)); + lb -> lb.computeStateForProgram(program)); } } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointEnablementTableCellEditor.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointStateTableCellEditor.java similarity index 75% rename from Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointEnablementTableCellEditor.java rename to Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointStateTableCellEditor.java index 2a136d91f8..90f34e4b39 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointEnablementTableCellEditor.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointStateTableCellEditor.java @@ -24,18 +24,18 @@ import javax.swing.plaf.basic.BasicButtonUI; import javax.swing.table.TableCellEditor; import docking.widgets.table.GTableFilterPanel; -import ghidra.app.services.LogicalBreakpoint.Enablement; +import ghidra.app.services.LogicalBreakpoint.State; -public class DebuggerBreakpointEnablementTableCellEditor extends AbstractCellEditor +public abstract class DebuggerBreakpointStateTableCellEditor extends AbstractCellEditor implements TableCellEditor, ActionListener { - private final GTableFilterPanel filterPanel; + private final GTableFilterPanel filterPanel; protected final JButton button = new JButton(); - private Enablement value = Enablement.NONE; - private LogicalBreakpointRow row; + private State value = State.NONE; + private T row; - public DebuggerBreakpointEnablementTableCellEditor( - GTableFilterPanel filterPanel) { + public DebuggerBreakpointStateTableCellEditor( + GTableFilterPanel filterPanel) { this.filterPanel = filterPanel; button.setHorizontalAlignment(SwingConstants.CENTER); @@ -62,16 +62,17 @@ public class DebuggerBreakpointEnablementTableCellEditor extends AbstractCellEdi button.setBackground(table.getBackground()); } this.row = filterPanel.getRowObject(row); - this.value = (Enablement) value; - button.setIcon(DebuggerBreakpointEnablementTableCellRenderer.iconForEnablement(this.value)); + this.value = (State) value; + button.setIcon(this.value.icon); button.setHorizontalAlignment(SwingConstants.CENTER); return button; } @Override public void actionPerformed(ActionEvent e) { - boolean mapped = row.isMapped(); - value = value.getToggled(mapped); + value = getToggledState(row, value); fireEditingStopped(); } + + protected abstract State getToggledState(T row, State current); } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocEnabledTableCellRenderer.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointStateTableCellRenderer.java similarity index 61% rename from Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocEnabledTableCellRenderer.java rename to Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointStateTableCellRenderer.java index ed04b0b6da..f970509342 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointLocEnabledTableCellRenderer.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointStateTableCellRenderer.java @@ -17,41 +17,32 @@ package ghidra.app.plugin.core.debug.gui.breakpoint; import java.awt.Component; -import javax.swing.Icon; import javax.swing.SwingConstants; import docking.widgets.table.GTableCellRenderingData; -import ghidra.app.plugin.core.debug.gui.DebuggerResources; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.docking.settings.Settings; import ghidra.util.table.column.AbstractGColumnRenderer; -public class DebuggerBreakpointLocEnabledTableCellRenderer - extends AbstractGColumnRenderer { +public class DebuggerBreakpointStateTableCellRenderer extends AbstractGColumnRenderer { - protected static Icon iconForEnabled(Boolean enabled) { - return enabled == null ? null - : enabled - ? DebuggerResources.ICON_BREAKPOINT_ENABLED_MARKER - : DebuggerResources.ICON_BREAKPOINT_DISABLED_MARKER; - } - - public DebuggerBreakpointLocEnabledTableCellRenderer() { + public DebuggerBreakpointStateTableCellRenderer() { setHorizontalAlignment(SwingConstants.CENTER); } @Override public Component getTableCellRendererComponent(GTableCellRenderingData data) { super.getTableCellRendererComponent(data); - Boolean en = (Boolean) data.getValue(); - setIcon(iconForEnabled(en)); + State state = (State) data.getValue(); + setIcon(state.icon); setHorizontalAlignment(SwingConstants.CENTER); setText(""); - setToolTipText(en ? "ENABLED" : "DISABLED"); + setToolTipText(state.name()); return this; } @Override - public String getFilterString(Boolean t, Settings settings) { - return t == null ? "null" : t ? "enabled" : "disabled"; + public String getFilterString(State t, Settings settings) { + return t.name(); } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java index f250acc9f2..c27f2a0187 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java @@ -36,7 +36,7 @@ import ghidra.app.plugin.core.debug.DebuggerPluginPackage; import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.app.plugin.core.debug.gui.DebuggerResources.*; import ghidra.app.services.*; -import ghidra.app.services.LogicalBreakpoint.Enablement; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.framework.model.DomainObject; import ghidra.framework.plugintool.AutoService; import ghidra.framework.plugintool.ComponentProviderAdapter; @@ -59,8 +59,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter protected enum LogicalBreakpointTableColumns implements EnumeratedTableColumn { - ENABLED("", Enablement.class, LogicalBreakpointRow::getEnablement, // - LogicalBreakpointRow::setEnablement, true), + STATE("State", State.class, LogicalBreakpointRow::getState, LogicalBreakpointRow::setState, true), NAME("Name", String.class, LogicalBreakpointRow::getName, LogicalBreakpointRow::setName, // LogicalBreakpointRow::isNamable, true), ADDRESS("Address", Address.class, LogicalBreakpointRow::getAddress, true), @@ -147,7 +146,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter protected enum BreakpointLocationTableColumns implements EnumeratedTableColumn { - ENABLED("", Boolean.class, BreakpointLocationRow::isEnabled, BreakpointLocationRow::setEnabled, true), + STATE("State", State.class, BreakpointLocationRow::getState, BreakpointLocationRow::setState, true), NAME("Name", String.class, BreakpointLocationRow::getName, BreakpointLocationRow::setName, true), ADDRESS("Address", Address.class, BreakpointLocationRow::getAddress, true), TRACE("Trace", String.class, BreakpointLocationRow::getTraceName, true), @@ -222,23 +221,29 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter } } - protected static boolean contextIsNonEmptyBreakpoints(ActionContext context) { + protected static boolean contextHasMatchingBreakpoints(ActionContext context, + Predicate logicalCase, + Predicate locationCase) { if (context == null) { return false; } if (context instanceof DebuggerLogicalBreakpointsActionContext) { DebuggerLogicalBreakpointsActionContext ctx = (DebuggerLogicalBreakpointsActionContext) context; - return !ctx.getSelection().isEmpty(); + return ctx.getSelection().stream().anyMatch(logicalCase); } if (context instanceof DebuggerBreakpointLocationsActionContext) { DebuggerBreakpointLocationsActionContext ctx = (DebuggerBreakpointLocationsActionContext) context; - return !ctx.getSelection().isEmpty(); + return ctx.getSelection().stream().anyMatch(locationCase); } return false; } + protected static boolean contextIsNonEmptyBreakpoints(ActionContext context) { + return contextHasMatchingBreakpoints(context, lb -> true, loc -> true); + } + protected class EnableSelectedBreakpointsAction extends AbstractEnableSelectedBreakpointsAction { public static final String GROUP = DebuggerResources.GROUP_BREAKPOINTS; @@ -246,7 +251,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter public EnableSelectedBreakpointsAction() { super(plugin); setToolBarData(new ToolBarData(ICON, GROUP)); - setPopupMenuData(new MenuData(new String[] { NAME }, GROUP)); + setPopupMenuData(new MenuData(new String[] { NAME }, ICON, GROUP)); addLocalAction(this); setEnabled(true); } @@ -259,7 +264,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter if (context instanceof DebuggerLogicalBreakpointsActionContext) { DebuggerLogicalBreakpointsActionContext ctx = (DebuggerLogicalBreakpointsActionContext) context; - Collection sel = ctx.getSelection(); + Collection sel = ctx.getBreakpoints(); breakpointService.enableAll(sel, null).exceptionally(ex -> { breakpointError("Enable Breakpoints", "Could not enable breakpoints", ex); return null; @@ -268,7 +273,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter if (context instanceof DebuggerBreakpointLocationsActionContext) { DebuggerBreakpointLocationsActionContext ctx = (DebuggerBreakpointLocationsActionContext) context; - Collection sel = ctx.getSelection(); + Collection sel = ctx.getLocations(); breakpointService.enableLocs(sel).exceptionally(ex -> { breakpointError("Enable Breakpoints", "Could not enable breakpoints", ex); return null; @@ -278,7 +283,14 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter @Override public boolean isEnabledForContext(ActionContext context) { - return contextIsNonEmptyBreakpoints(context); + return contextHasMatchingBreakpoints(context, + row -> row.getState() != State.ENABLED, + row -> row.getState() != State.ENABLED); + } + + @Override + public boolean isAddToPopup(ActionContext context) { + return isEnabledForContext(context); } } @@ -314,7 +326,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter public DisableSelectedBreakpointsAction() { super(plugin); setToolBarData(new ToolBarData(ICON, GROUP)); - setPopupMenuData(new MenuData(new String[] { NAME }, GROUP)); + setPopupMenuData(new MenuData(new String[] { NAME }, ICON, GROUP)); addLocalAction(this); setEnabled(true); } @@ -327,7 +339,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter if (context instanceof DebuggerLogicalBreakpointsActionContext) { DebuggerLogicalBreakpointsActionContext ctx = (DebuggerLogicalBreakpointsActionContext) context; - Collection sel = ctx.getSelection(); + Collection sel = ctx.getBreakpoints(); breakpointService.disableAll(sel, null).exceptionally(ex -> { breakpointError("Disable Breakpoints", "Could not disable breakpoints", ex); return null; @@ -336,7 +348,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter if (context instanceof DebuggerBreakpointLocationsActionContext) { DebuggerBreakpointLocationsActionContext ctx = (DebuggerBreakpointLocationsActionContext) context; - Collection sel = ctx.getSelection(); + Collection sel = ctx.getLocations(); breakpointService.disableLocs(sel).exceptionally(ex -> { breakpointError("Disable Breakpoints", "Could not disable breakpoints", ex); return null; @@ -346,8 +358,16 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter @Override public boolean isEnabledForContext(ActionContext context) { - return contextIsNonEmptyBreakpoints(context); + return contextHasMatchingBreakpoints(context, + row -> row.getState() != State.DISABLED, + row -> row.getState() != State.DISABLED); } + + @Override + public boolean isAddToPopup(ActionContext context) { + return isEnabledForContext(context); + } + } protected class DisableAllBreakpointsAction extends AbstractDisableAllBreakpointsAction { @@ -381,7 +401,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter public ClearSelectedBreakpointsAction() { super(plugin); setToolBarData(new ToolBarData(ICON, GROUP)); - setPopupMenuData(new MenuData(new String[] { NAME }, GROUP)); + setPopupMenuData(new MenuData(new String[] { NAME }, ICON, GROUP)); addLocalAction(this); setEnabled(true); } @@ -391,7 +411,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter if (context instanceof DebuggerLogicalBreakpointsActionContext) { DebuggerLogicalBreakpointsActionContext ctx = (DebuggerLogicalBreakpointsActionContext) context; - Collection sel = ctx.getSelection(); + Collection sel = ctx.getBreakpoints(); breakpointService.deleteAll(sel, null).exceptionally(ex -> { breakpointError("Clear Breakpoints", "Could not clear breakpoints", ex); return null; @@ -400,7 +420,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter if (context instanceof DebuggerBreakpointLocationsActionContext) { DebuggerBreakpointLocationsActionContext ctx = (DebuggerBreakpointLocationsActionContext) context; - Collection sel = ctx.getSelection(); + Collection sel = ctx.getLocations(); breakpointService.deleteLocs(sel).exceptionally(ex -> { breakpointError("Clear Breakpoints", "Could not clear breakpoints", ex); return null; @@ -452,7 +472,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter public void actionPerformed(ActionContext context) { Set enablable = breakpointService.getAllBreakpoints() .stream() - .filter(lb -> lb.computeEnablement() == Enablement.INEFFECTIVE_ENABLED && + .filter(lb -> lb.computeState() == State.INEFFECTIVE_ENABLED && !lb.getMappedTraces().isEmpty()) .collect(Collectors.toSet()); breakpointService.enableAll(enablable, null).exceptionally(ex -> { @@ -476,7 +496,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter } Set all = breakpointService.getAllBreakpoints(); for (LogicalBreakpoint lb : all) { - if (lb.computeEnablement() != Enablement.INEFFECTIVE_ENABLED) { + if (lb.computeState() != State.INEFFECTIVE_ENABLED) { continue; } if (lb.getMappedTraces().isEmpty()) { @@ -609,7 +629,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter private final DebuggerBreakpointsPlugin plugin; // @AutoServiceConsumed via method - private DebuggerLogicalBreakpointService breakpointService; + DebuggerLogicalBreakpointService breakpointService; // @AutoServiceConsumed via method, package access for BreakpointLogicalRow DebuggerModelService modelService; @AutoServiceConsumed @@ -763,6 +783,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter public void breakpointUpdated(LogicalBreakpoint lb) { Swing.runIfSwingOrRunLater(() -> { breakpointTableModel.updateItem(lb); + breakpointLocationsUpdated(lb.getTraceBreakpoints()); contextChanged(); }); } @@ -771,6 +792,9 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter public void breakpointsUpdated(Collection clb) { Swing.runIfSwingOrRunLater(() -> { breakpointTableModel.updateAllItems(clb); + breakpointLocationsUpdated(clb.stream() + .flatMap(lb -> lb.getTraceBreakpoints().stream()) + .collect(Collectors.toSet())); contextChanged(); }); } @@ -817,6 +841,10 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter locationTableModel.updateItem(location); } + private void breakpointLocationsUpdated(Collection locations) { + locationTableModel.updateAllItems(locations); + } + private void breakpointLocationRemoved(TraceBreakpoint location) { locationTableModel.deleteItem(location); } @@ -891,16 +919,13 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter mainPanel.setResizeWeight(0.5); breakpointTable.getSelectionModel().addListSelectionListener(evt -> { - List set = breakpointFilterPanel.getSelectedItems() - .stream() - .map(LogicalBreakpointRow::getLogicalBreakpoint) - .collect(Collectors.toList()); + List sel = breakpointFilterPanel.getSelectedItems(); // Do this first to prevent overriding context in event chain - if (!set.isEmpty()) { + if (!sel.isEmpty()) { locationTable.clearSelection(); locationTable.getSelectionManager().clearSavedSelection(); } - myActionContext = new DebuggerLogicalBreakpointsActionContext(set); + myActionContext = new DebuggerLogicalBreakpointsActionContext(sel); if (isFilterLocationsByBreakpoints()) { locationTableModel.fireTableDataChanged(); } @@ -930,16 +955,13 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter }); locationTable.getSelectionModel().addListSelectionListener(evt -> { - List set = locationFilterPanel.getSelectedItems() - .stream() - .map(BreakpointLocationRow::getTraceBreakpoint) - .collect(Collectors.toList()); + List sel = locationFilterPanel.getSelectedItems(); // Do this first to avoid overriding context in event chain - if (!set.isEmpty()) { + if (!sel.isEmpty()) { breakpointTable.clearSelection(); breakpointTable.getSelectionManager().clearSavedSelection(); } - myActionContext = new DebuggerBreakpointLocationsActionContext(set); + myActionContext = new DebuggerBreakpointLocationsActionContext(sel); contextChanged(); }); locationTable.addMouseListener(new MouseAdapter() { @@ -966,10 +988,15 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter TableColumnModel bptColModel = breakpointTable.getColumnModel(); TableColumn bptEnCol = - bptColModel.getColumn(LogicalBreakpointTableColumns.ENABLED.ordinal()); - bptEnCol.setCellRenderer(new DebuggerBreakpointEnablementTableCellRenderer()); + bptColModel.getColumn(LogicalBreakpointTableColumns.STATE.ordinal()); + bptEnCol.setCellRenderer(new DebuggerBreakpointStateTableCellRenderer()); bptEnCol.setCellEditor( - new DebuggerBreakpointEnablementTableCellEditor(breakpointFilterPanel)); + new DebuggerBreakpointStateTableCellEditor<>(breakpointFilterPanel) { + @Override + protected State getToggledState(LogicalBreakpointRow row, State current) { + return current.getToggled(row.isMapped()); + } + }); bptEnCol.setMaxWidth(24); bptEnCol.setMinWidth(24); TableColumn bptNameCol = @@ -991,16 +1018,24 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter bptColModel.getColumn(LogicalBreakpointTableColumns.LOCATIONS.ordinal()); locsCol.setPreferredWidth(20); - TableColumnModel locColModel = locationTable.getColumnModel(); + GTableColumnModel locColModel = (GTableColumnModel) locationTable.getColumnModel(); TableColumn locEnCol = - locColModel.getColumn(BreakpointLocationTableColumns.ENABLED.ordinal()); - locEnCol.setCellRenderer(new DebuggerBreakpointLocEnabledTableCellRenderer()); - locEnCol.setCellEditor(new DebuggerBreakpointLocEnabledTableCellEditor()); + locColModel.getColumn(BreakpointLocationTableColumns.STATE.ordinal()); + locEnCol.setCellRenderer(new DebuggerBreakpointStateTableCellRenderer()); + locEnCol.setCellEditor(new DebuggerBreakpointStateTableCellEditor<>(locationFilterPanel) { + @Override + protected State getToggledState(BreakpointLocationRow row, State current) { + return current.getToggled(false); + } + }); locEnCol.setMaxWidth(24); locEnCol.setMinWidth(24); TableColumn locAddrCol = locColModel.getColumn(BreakpointLocationTableColumns.ADDRESS.ordinal()); locAddrCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT); + TableColumn locThreadsCol = + locColModel.getColumn(BreakpointLocationTableColumns.THREADS.ordinal()); + locColModel.setVisible(locThreadsCol, false); } protected void navigateToSelectedBreakpoint() { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerLogicalBreakpointsActionContext.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerLogicalBreakpointsActionContext.java index 9b865934e5..5798c96979 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerLogicalBreakpointsActionContext.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerLogicalBreakpointsActionContext.java @@ -16,18 +16,25 @@ package ghidra.app.plugin.core.debug.gui.breakpoint; import java.util.Collection; +import java.util.stream.Collectors; import docking.ActionContext; import ghidra.app.services.LogicalBreakpoint; public class DebuggerLogicalBreakpointsActionContext extends ActionContext { - private final Collection selection; + private final Collection selection; - public DebuggerLogicalBreakpointsActionContext(Collection selection) { + public DebuggerLogicalBreakpointsActionContext(Collection selection) { this.selection = selection; } - public Collection getSelection() { + public Collection getSelection() { return selection; } + + public Collection getBreakpoints() { + return selection.stream() + .map(row -> row.getLogicalBreakpoint()) + .collect(Collectors.toList()); + } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/LogicalBreakpointRow.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/LogicalBreakpointRow.java index 6af9fc4a67..21124be93e 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/LogicalBreakpointRow.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/LogicalBreakpointRow.java @@ -18,7 +18,8 @@ package ghidra.app.plugin.core.debug.gui.breakpoint; import java.util.concurrent.CompletableFuture; import ghidra.app.services.LogicalBreakpoint; -import ghidra.app.services.LogicalBreakpoint.Enablement; +import ghidra.app.services.LogicalBreakpoint.Mode; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.framework.model.DomainFile; import ghidra.framework.model.DomainObject; import ghidra.program.model.address.Address; @@ -43,23 +44,19 @@ public class LogicalBreakpointRow { return lb; } - public Enablement getEnablement() { + public State getState() { return provider.isFilterByCurrentTrace() && provider.currentTrace != null - ? lb.computeEnablementForTrace(provider.currentTrace) - : lb.computeEnablement(); + ? lb.computeStateForTrace(provider.currentTrace) + : lb.computeState(); } - public void setEnablement(Enablement en) { - assert en.consistent && en.effective; - setEnabled(en.enabled); + public void setState(State state) { + assert state.isNormal(); + setEnabled(state.isEnabled()); } - public Boolean isEnabled() { - Enablement en = getEnablement(); - if (!en.consistent) { - return null; - } - return en.enabled && en.effective; + public Mode getMode() { + return getState().mode; } public void setEnabled(boolean enabled) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/ObjectTree.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/ObjectTree.java index c6ad2933f2..371101b879 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/ObjectTree.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/components/ObjectTree.java @@ -47,7 +47,7 @@ import resources.ResourceManager; public class ObjectTree implements ObjectPane { public static final ImageIcon ICON_TREE = - ResourceManager.loadImage("images/breakpoint-disable.png"); + ResourceManager.loadImage("images/object-unpopulated.png"); private ObjectNode root; private GTree tree; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServicePlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServicePlugin.java index e88bfe088d..4a9e099ef3 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServicePlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServicePlugin.java @@ -19,6 +19,7 @@ import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.stream.Collectors; import org.apache.commons.collections4.IteratorUtils; @@ -33,6 +34,7 @@ import ghidra.app.plugin.core.debug.event.TraceClosedPluginEvent; import ghidra.app.plugin.core.debug.event.TraceOpenedPluginEvent; import ghidra.app.plugin.core.debug.service.breakpoint.LogicalBreakpointInternal.ProgramBreakpoint; import ghidra.app.services.*; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.dbg.target.TargetBreakpointLocation; import ghidra.dbg.target.TargetObject; import ghidra.dbg.util.PathUtils; @@ -368,7 +370,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin public AbstractInfo() { } - protected abstract void dispose(); + protected abstract void dispose(RemoveCollector r); protected abstract LogicalBreakpointInternal createLogicalBreakpoint(Address address, long length, Collection kinds); @@ -489,12 +491,9 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin } @Override - protected void dispose() { + protected void dispose(RemoveCollector r) { trace.removeListener(breakpointListener); - - try (RemoveCollector r = new RemoveCollector(changeListeners.fire)) { - forgetAllBreakpoints(r); - } + forgetAllBreakpoints(r); } protected void reloadBreakpoints() { @@ -559,7 +558,8 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin trackTraceBreakpoint(b, collector, false); } catch (TrackedTooSoonException e) { - throw new AssertionError(e); + // This can still happen during reload (on OBJECT_RESTORED) + Msg.warn(this, "Might have lost track of a breakpoint: " + b); } } } @@ -592,7 +592,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin lb = getOrCreateLogicalBreakpointFor(traceAddr, breakpoint, c); } assert breakpointsByAddress.get(traceAddr).contains(lb); - if (forceUpdate || lb.trackBreakpoint(breakpoint)) { + if (lb.trackBreakpoint(breakpoint) || forceUpdate) { c.updated(lb); } } @@ -691,12 +691,9 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin } @Override - protected void dispose() { + protected void dispose(RemoveCollector r) { program.removeListener(breakpointListener); - - try (RemoveCollector r = new RemoveCollector(changeListeners.fire)) { - forgetAllBreakpoints(r); - } + forgetAllBreakpoints(r); } protected void reloadBreakpoints() { @@ -877,8 +874,11 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin if (program instanceof TraceProgramView) { return; } - programInfos.remove(program).dispose(); - // The mapping removals, if applicable, will clean up related traces + + try (RemoveCollector r = new RemoveCollector(changeListeners.fire)) { + programInfos.remove(program).dispose(r); + } + // The mapping removals, if any, will clean up related traces } } @@ -894,9 +894,20 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin private void doUntrackTrace(Trace trace) { synchronized (lock) { - InfoPerTrace info = traceInfos.remove(trace); - if (info != null) { // Could be from trace close or recorder stop - info.dispose(); + InfoPerTrace tInfo = traceInfos.remove(trace); + if (tInfo == null) { // Could be from trace close or recorder stop + return; + } + try (RemoveCollector r = new RemoveCollector(changeListeners.fire)) { + tInfo.dispose(r); + for (InfoPerProgram pInfo : programInfos.values()) { + for (Set set : pInfo.breakpointsByAddress.values()) { + for (LogicalBreakpointInternal lb : set) { + lb.removeTrace(trace); + r.updated(lb); + } + } + } } } } @@ -1001,10 +1012,25 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin if (info == null) { return Set.of(); } - return doGetBreakpointsAt(info, address); + return doGetBreakpointsAt(info, address).stream() + .filter(lb -> lb.computeStateForTrace(trace) != State.NONE) + .collect(Collectors.toSet()); } } + @Override + public LogicalBreakpoint getBreakpoint(TraceBreakpoint bpt) { + Trace trace = bpt.getTrace(); + synchronized (lock) { + for (LogicalBreakpoint lb : getBreakpointsAt(trace, bpt.getMinAddress())) { + if (lb.getTraceBreakpoints(trace).contains(bpt)) { + return lb; + } + } + } + return null; + } + @Override public Set getBreakpointsAt(ProgramLocation loc) { return DebuggerLogicalBreakpointService.programOrTrace(loc, @@ -1022,13 +1048,13 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin changeListeners.remove(l); } - @Override - public CompletableFuture placeBreakpointAt(Program program, Address address, long length, - Collection kinds, String name) { + protected MappedLogicalBreakpoint synthesizeLogicalBreakpoint(Program program, Address address, + long length, Collection kinds) { /** - * TODO: This is quite a waste, but it gets the job done. The instance created here is - * dropped. It's just used for its breakpoint placement logic, part of which I had to - * re-implement here, anyway. The actual logical breakpoint is created by event processors. + * TODO: This is a bit of a waste, but it gets the job done. The instance created here is + * dropped by its caller. It's just used for its breakpoint placement logic, part of which I + * had to re-implement here, anyway. The actual logical breakpoint is created by event + * processors. */ MappedLogicalBreakpoint lb = new MappedLogicalBreakpoint(program, address, length, kinds); synchronized (lock) { @@ -1040,21 +1066,36 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin lb.setTraceAddress(ti.recorder, loc.getAddress()); } } + return lb; + } + + @Override + public CompletableFuture placeBreakpointAt(Program program, Address address, long length, + Collection kinds, String name) { + MappedLogicalBreakpoint lb = synthesizeLogicalBreakpoint(program, address, length, kinds); return lb.enableWithName(name); } @Override public CompletableFuture placeBreakpointAt(Trace trace, Address address, long length, - Collection kinds) { + Collection kinds, String name) { TraceRecorder recorder = modelService.getRecorder(trace); if (recorder == null) { throw new IllegalArgumentException("Given trace is not live"); } - /** - * TODO: This is similarly wasteful, but given the complexity of the breakpoint placement - * logic it offers, this works really well, and is probably justified. - */ - return new LoneLogicalBreakpoint(recorder, address, length, kinds).enableForTrace(trace); + + ProgramLocation staticLocation = mappingService.getOpenMappedLocation( + new DefaultTraceLocation(trace, null, Range.singleton(recorder.getSnap()), address)); + if (staticLocation == null) { + return new LoneLogicalBreakpoint(recorder, address, length, kinds) + .enableForTrace(trace); + } + + MappedLogicalBreakpoint lb = new MappedLogicalBreakpoint(staticLocation.getProgram(), + staticLocation.getAddress(), length, kinds); + lb.setTraceAddress(recorder, address); + lb.enableForProgramWithName(name); + return lb.enableForTrace(trace); } @Override @@ -1062,7 +1103,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin Collection kinds, String name) { return DebuggerLogicalBreakpointService.programOrTrace(loc, (p, a) -> placeBreakpointAt(p, a, length, kinds, name), - (t, a) -> placeBreakpointAt(t, a, length, kinds)); + (t, a) -> placeBreakpointAt(t, a, length, kinds, name)); } protected CompletableFuture actOnAll(Collection col, Trace trace, @@ -1070,7 +1111,8 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin BiConsumer consumerForTarget) { BreakpointActionSet actions = new BreakpointActionSet(); for (LogicalBreakpoint lb : col) { - if (trace == null) { + Set participants = lb.getParticipatingTraces(); + if (trace == null || participants.isEmpty() || participants.equals(Set.of(trace))) { consumerForProgram.accept(lb); } if (!(lb instanceof LogicalBreakpointInternal)) { @@ -1096,14 +1138,23 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin @Override public CompletableFuture deleteAll(Collection col, Trace trace) { - return actOnAll(col, trace, LogicalBreakpoint::deleteForProgram, - (actions, lbi) -> lbi.planDelete(actions, trace)); + return actOnAll(col, trace, lb -> { + // Do not delete bookmark from dynamic context + if (trace == null) { + lb.deleteForProgram(); + } + }, (actions, lbi) -> lbi.planDelete(actions, trace)); } protected CompletableFuture actOnLocs(Collection col, - BiConsumer consumer) { + BiConsumer locConsumer, + Consumer progConsumer) { BreakpointActionSet actions = new BreakpointActionSet(); for (TraceBreakpoint tb : col) { + LogicalBreakpoint lb = getBreakpoint(tb); + if (col.containsAll(lb.getTraceBreakpoints())) { + progConsumer.accept(lb); + } TraceRecorder recorder = modelService.getRecorder(tb.getTrace()); if (recorder == null) { continue; @@ -1115,24 +1166,27 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin continue; } TargetBreakpointLocation loc = (TargetBreakpointLocation) object; - consumer.accept(actions, loc); + locConsumer.accept(actions, loc); } return actions.execute(); } @Override public CompletableFuture enableLocs(Collection col) { - return actOnLocs(col, BreakpointActionSet::planEnable); + return actOnLocs(col, BreakpointActionSet::planEnable, LogicalBreakpoint::enableForProgram); } @Override public CompletableFuture disableLocs(Collection col) { - return actOnLocs(col, BreakpointActionSet::planDisable); + return actOnLocs(col, BreakpointActionSet::planDisable, + LogicalBreakpoint::disableForProgram); } @Override public CompletableFuture deleteLocs(Collection col) { - return actOnLocs(col, BreakpointActionSet::planDelete); + return actOnLocs(col, BreakpointActionSet::planDelete, lb -> { + // Never delete bookmark when user requests deleting locations + }); } @Override diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/LogicalBreakpointInternal.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/LogicalBreakpointInternal.java index d9e9cd6939..2c976714ed 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/LogicalBreakpointInternal.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/LogicalBreakpointInternal.java @@ -134,15 +134,15 @@ public interface LogicalBreakpointInternal extends LogicalBreakpoint { } } - public ProgramEnablement computeEnablement() { + public ProgramMode computeMode() { if (eBookmark != null) { - return ProgramEnablement.ENABLED; + return ProgramMode.ENABLED; } if (dBookmark != null) { - return ProgramEnablement.DISABLED; + return ProgramMode.DISABLED; } else { - return ProgramEnablement.MISSING; + return ProgramMode.MISSING; } } @@ -233,11 +233,11 @@ public interface LogicalBreakpointInternal extends LogicalBreakpoint { } public boolean isEnabled() { - return computeEnablement() == ProgramEnablement.ENABLED; + return computeMode() == ProgramMode.ENABLED; } public boolean isDisabled() { - return computeEnablement() == ProgramEnablement.DISABLED; + return computeMode() == ProgramMode.DISABLED; } public String computeCategory() { @@ -313,19 +313,22 @@ public interface LogicalBreakpointInternal extends LogicalBreakpoint { } public Address computeTargetAddress() { - // TODO: Can this change? If not, can compute in constructor. return recorder.getMemoryMapper().traceToTarget(address); } - public TraceEnablement computeEnablement() { - TraceEnablement en = TraceEnablement.MISSING; + public TraceMode computeMode() { + TraceMode mode = TraceMode.NONE; for (IDHashed bpt : breakpoints) { - en = en.combine(TraceEnablement.fromBool(bpt.obj.isEnabled(recorder.getSnap()))); - if (en == TraceEnablement.MIXED) { - return en; + mode = mode.combine(computeMode(bpt.obj)); + if (mode == TraceMode.MISSING) { + return mode; } } - return en; + return mode; + } + + public TraceMode computeMode(TraceBreakpoint bpt) { + return TraceMode.fromBool(bpt.isEnabled(recorder.getSnap())); } public boolean isEmpty() { @@ -446,6 +449,16 @@ public interface LogicalBreakpointInternal extends LogicalBreakpoint { */ void setTraceAddress(TraceRecorder recorder, Address address); + /** + * Remove the given trace from this set + * + *

+ * This happens when a trace's recorder stops or when a trace is closed. + * + * @param trace the trace no longer participating + */ + void removeTrace(Trace trace); + boolean canMerge(Program program, Bookmark bookmark); /** diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/LoneLogicalBreakpoint.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/LoneLogicalBreakpoint.java index 7c687c667d..9312271557 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/LoneLogicalBreakpoint.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/LoneLogicalBreakpoint.java @@ -89,6 +89,11 @@ public class LoneLogicalBreakpoint implements LogicalBreakpointInternal { throw new AssertionError(); } + @Override + public void removeTrace(Trace trace) { + throw new AssertionError(); + } + @Override public Set getTraceBreakpoints() { return new HashSet<>(breaks.getBreakpoints()); @@ -131,21 +136,29 @@ public class LoneLogicalBreakpoint implements LogicalBreakpointInternal { } @Override - public Enablement computeEnablementForProgram(Program program) { - return Enablement.NONE; + public State computeStateForProgram(Program program) { + return State.NONE; } @Override - public Enablement computeEnablementForTrace(Trace trace) { + public State computeStateForTrace(Trace trace) { if (trace != breaks.getTrace()) { - return Enablement.NONE; + return State.NONE; } - return ProgramEnablement.NONE.combineTrace(breaks.computeEnablement()); + return ProgramMode.NONE.combineTrace(breaks.computeMode(), Perspective.TRACE); } @Override - public Enablement computeEnablement() { - return ProgramEnablement.NONE.combineTrace(breaks.computeEnablement()); + public State computeStateForLocation(TraceBreakpoint loc) { + if (!breaks.getBreakpoints().contains(loc)) { + return State.NONE; + } + return ProgramMode.NONE.combineTrace(breaks.computeMode(loc), Perspective.TRACE); + } + + @Override + public State computeState() { + return ProgramMode.NONE.combineTrace(breaks.computeMode(), Perspective.LOGICAL); } @Override diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/MappedLogicalBreakpoint.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/MappedLogicalBreakpoint.java index ee415dc731..142c939a7d 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/MappedLogicalBreakpoint.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/breakpoint/MappedLogicalBreakpoint.java @@ -40,7 +40,6 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { // Debuggers tend to allow multiple breakpoints (even of the same kind) at the same address // They may have different names and/or different conditions private final Map traceBreaks = new HashMap<>(); - private final Set tracesView = Collections.unmodifiableSet(traceBreaks.keySet()); protected MappedLogicalBreakpoint(Program program, Address progAddr, long length, Collection kinds) { @@ -51,8 +50,10 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { @Override public String toString() { - return String.format("<%s prog=%s, traces=%s>", getClass().getSimpleName(), progBreak, - traceBreaks.values()); + synchronized (traceBreaks) { + return String.format("<%s prog=%s, traces=%s>", getClass().getSimpleName(), progBreak, + traceBreaks.values()); + } } protected boolean hasProgramBreakpoint() { @@ -64,9 +65,11 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { if (!progBreak.isEmpty()) { return false; } - for (TraceBreakpointSet breaks : traceBreaks.values()) { - if (!breaks.isEmpty()) { - return false; + synchronized (traceBreaks) { + for (TraceBreakpointSet breaks : traceBreaks.values()) { + if (!breaks.isEmpty()) { + return false; + } } } return true; @@ -100,6 +103,10 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { progBreak.enable(); } + public void enableForProgramWithName(String name) { + progBreak.enableWithComment(name); + } + @Override public void disableForProgram() { progBreak.disable(); @@ -113,7 +120,10 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { @Override public CompletableFuture enableForTrace(Trace trace) { BreakpointActionSet actions = new BreakpointActionSet(); - TraceBreakpointSet breaks = traceBreaks.get(trace); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(trace); + } if (breaks == null) { return AsyncUtils.NIL; } @@ -124,7 +134,10 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { @Override public CompletableFuture disableForTrace(Trace trace) { BreakpointActionSet actions = new BreakpointActionSet(); - TraceBreakpointSet breaks = traceBreaks.get(trace); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(trace); + } if (breaks == null) { return AsyncUtils.NIL; } @@ -135,7 +148,10 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { @Override public CompletableFuture deleteForTrace(Trace trace) { BreakpointActionSet actions = new BreakpointActionSet(); - TraceBreakpointSet breaks = traceBreaks.get(trace); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(trace); + } if (breaks == null) { return AsyncUtils.NIL; } @@ -146,15 +162,20 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { @Override public void planEnable(BreakpointActionSet actions, Trace trace) { if (trace != null) { - TraceBreakpointSet breaks = traceBreaks.get(trace); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(trace); + } if (breaks == null) { return; } breaks.planEnable(actions, length, kinds); return; } - for (TraceBreakpointSet breaks : traceBreaks.values()) { - breaks.planEnable(actions, length, kinds); + synchronized (traceBreaks) { + for (TraceBreakpointSet breaks : traceBreaks.values()) { + breaks.planEnable(actions, length, kinds); + } } } @@ -167,28 +188,33 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { public CompletableFuture enableWithName(String name) { // TODO: Consider more fields than name in comment - progBreak.enableWithComment(name); + enableForProgramWithName(name); return enableForTraces(); } @Override public CompletableFuture enable() { - progBreak.enable(); + enableForProgram(); return enableForTraces(); } @Override public void planDisable(BreakpointActionSet actions, Trace trace) { if (trace != null) { - TraceBreakpointSet breaks = traceBreaks.get(trace); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(trace); + } if (breaks == null) { return; } breaks.planDisable(actions, length, kinds); return; } - for (TraceBreakpointSet breaks : traceBreaks.values()) { - breaks.planDisable(actions, length, kinds); + synchronized (traceBreaks) { + for (TraceBreakpointSet breaks : traceBreaks.values()) { + breaks.planDisable(actions, length, kinds); + } } } @@ -204,15 +230,20 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { @Override public void planDelete(BreakpointActionSet actions, Trace trace) { if (trace != null) { - TraceBreakpointSet breaks = traceBreaks.get(trace); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(trace); + } if (breaks == null) { return; } breaks.planDelete(actions, length, kinds); return; } - for (TraceBreakpointSet breaks : traceBreaks.values()) { - breaks.planDelete(actions, length, kinds); + synchronized (traceBreaks) { + for (TraceBreakpointSet breaks : traceBreaks.values()) { + breaks.planDelete(actions, length, kinds); + } } } @@ -247,44 +278,65 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { @Override public void setTraceAddress(TraceRecorder recorder, Address address) { - traceBreaks.put(recorder.getTrace(), new TraceBreakpointSet(recorder, address)); + synchronized (traceBreaks) { + traceBreaks.put(recorder.getTrace(), new TraceBreakpointSet(recorder, address)); + } + } + + @Override + public void removeTrace(Trace trace) { + synchronized (traceBreaks) { + traceBreaks.remove(trace); + } } @Override public Set getTraceBreakpoints() { Set result = new HashSet<>(); - for (TraceBreakpointSet breaks : traceBreaks.values()) { - result.addAll(breaks.getBreakpoints()); + synchronized (traceBreaks) { + for (TraceBreakpointSet breaks : traceBreaks.values()) { + result.addAll(breaks.getBreakpoints()); + } } return result; } @Override public Set getTraceBreakpoints(Trace trace) { - TraceBreakpointSet breaks = traceBreaks.get(trace); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(trace); + } return breaks == null ? Set.of() : new HashSet<>(breaks.getBreakpoints()); } @Override public Set getMappedTraces() { - return tracesView; + synchronized (traceBreaks) { + return Set.copyOf(traceBreaks.keySet()); + } } @Override public Set getParticipatingTraces() { Set result = new HashSet<>(); - for (TraceBreakpointSet breaks : traceBreaks.values()) { - if (breaks.isEmpty()) { - continue; + synchronized (traceBreaks) { + for (TraceBreakpointSet breaks : traceBreaks.values()) { + if (breaks.isEmpty()) { + continue; + } + result.add(breaks.getTrace()); } - result.add(breaks.getTrace()); } return result; } @Override public Address getTraceAddress(Trace trace) { - TraceBreakpointSet breaks = traceBreaks.get(trace); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(trace); + } if (breaks == null) { return null; } @@ -312,36 +364,61 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { } @Override - public Enablement computeEnablementForProgram(Program program) { + public State computeStateForProgram(Program program) { if (progBreak.getProgram() != program) { - return Enablement.NONE; + return State.NONE; } - return computeEnablement(); + return computeState(); } @Override - public Enablement computeEnablementForTrace(Trace trace) { - TraceBreakpointSet breaks = traceBreaks.get(trace); - ProgramEnablement progEn = progBreak.computeEnablement(); - if (breaks == null) { - return TraceEnablement.MISSING.combineProgram(progEn); + public State computeStateForTrace(Trace trace) { + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(trace); } - // NB: Order matters. Trace is primary - return breaks.computeEnablement().combineProgram(progEn); + ProgramMode progMode = progBreak.computeMode(); + TraceMode traceMode = breaks == null ? TraceMode.NONE : breaks.computeMode(); + return progMode.combineTrace(traceMode, Perspective.TRACE); + } + + protected TraceMode computeTraceModeForLocation(TraceBreakpoint loc) { + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(loc.getTrace()); + } + if (breaks == null || !breaks.getBreakpoints().contains(loc)) { + return TraceMode.NONE; + } + return breaks.computeMode(loc); } @Override - public Enablement computeEnablement() { - ProgramEnablement progEn = progBreak.computeEnablement(); - TraceEnablement traceEn = TraceEnablement.NONE; - for (TraceBreakpointSet breaks : traceBreaks.values()) { - TraceEnablement tEn = breaks.computeEnablement(); - traceEn = traceEn.combine(tEn); - if (traceEn == TraceEnablement.MIXED) { - break; + public State computeStateForLocation(TraceBreakpoint loc) { + ProgramMode progMode = progBreak.computeMode(); + TraceMode traceMode = computeTraceModeForLocation(loc); + return progMode.combineTrace(traceMode, Perspective.TRACE); + } + + protected TraceMode computeTraceMode() { + TraceMode traceMode = TraceMode.NONE; + synchronized (traceBreaks) { + for (TraceBreakpointSet breaks : traceBreaks.values()) { + TraceMode tm = breaks.computeMode(); + traceMode = traceMode.combine(tm); + if (traceMode == TraceMode.MISSING) { + break; + } } } - return progEn.combineTrace(traceEn); + return traceMode; + } + + @Override + public State computeState() { + ProgramMode progMode = progBreak.computeMode(); + TraceMode traceMode = computeTraceMode(); + return progMode.combineTrace(traceMode, Perspective.LOGICAL); } @Override @@ -351,7 +428,10 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { @Override public boolean canMerge(TraceBreakpoint breakpoint) throws TrackedTooSoonException { - TraceBreakpointSet breaks = traceBreaks.get(breakpoint.getTrace()); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(breakpoint.getTrace()); + } if (breaks == null) { /** * This happens when the trace is first added to the manager, between the listener being @@ -377,10 +457,25 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { return progBreak.add(bookmark); } + protected void makeBookmarkConsistent() { + TraceMode traceMode = computeTraceMode(); + if (traceMode == TraceMode.ENABLED) { + progBreak.enable(); + } + else if (traceMode == TraceMode.DISABLED) { + progBreak.disable(); + } + } + @Override public boolean trackBreakpoint(TraceBreakpoint breakpoint) { - TraceBreakpointSet breaks = traceBreaks.get(breakpoint.getTrace()); - return breaks.add(breakpoint); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(breakpoint.getTrace()); + } + boolean result = breaks.add(breakpoint); + makeBookmarkConsistent(); + return result; } @Override @@ -391,11 +486,16 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { @Override public boolean untrackBreakpoint(TraceBreakpoint breakpoint) { - TraceBreakpointSet breaks = traceBreaks.get(breakpoint.getTrace()); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(breakpoint.getTrace()); + } if (breaks == null) { return false; } - return breaks.remove(breakpoint); + boolean result = breaks.remove(breakpoint); + makeBookmarkConsistent(); + return result; } public boolean appliesTo(Program program) { @@ -403,7 +503,10 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal { } public boolean appliesTo(Trace trace) { - TraceBreakpointSet breaks = traceBreaks.get(Objects.requireNonNull(trace)); + TraceBreakpointSet breaks; + synchronized (traceBreaks) { + breaks = traceBreaks.get(Objects.requireNonNull(trace)); + } return !breaks.isEmpty(); } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerLogicalBreakpointService.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerLogicalBreakpointService.java index f0894526ac..946f419778 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerLogicalBreakpointService.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerLogicalBreakpointService.java @@ -20,7 +20,7 @@ import java.util.concurrent.CompletableFuture; import java.util.function.BiFunction; import ghidra.app.plugin.core.debug.service.breakpoint.DebuggerLogicalBreakpointServicePlugin; -import ghidra.app.services.LogicalBreakpoint.Enablement; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.framework.plugintool.ServiceInfo; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Program; @@ -90,6 +90,18 @@ public interface DebuggerLogicalBreakpointService { */ Set getBreakpointsAt(Trace trace, Address address); + /** + * Get the logical breakpoint of which the given trace breakpoint is a part + * + *

+ * If the given trace breakpoint is not part of any logical breakpoint, e.g., because it is not + * on a live target, then null is returned. + * + * @param bpt the trace breakpoint + * @return the logical breakpoint, or null + */ + LogicalBreakpoint getBreakpoint(TraceBreakpoint bpt); + /** * Get the collected logical breakpoints (at present) at the given location. * @@ -146,39 +158,45 @@ public interface DebuggerLogicalBreakpointService { return progFunc.apply(progOrView, loc.getByteAddress()); } - default Enablement computeEnablement(Collection col) { - Enablement en = Enablement.NONE; + default State computeState(Collection col) { + State state = State.NONE; for (LogicalBreakpoint lb : col) { - en = en.sameAdddress(lb.computeEnablement()); + state = state.sameAdddress(lb.computeState()); } - return en; + return state; } - default Enablement computeEnablement(Collection col, Program program) { - Enablement en = Enablement.NONE; + default State computeState(Collection col, Program program) { + State state = State.NONE; for (LogicalBreakpoint lb : col) { - en = en.sameAdddress(lb.computeEnablementForProgram(program)); + state = state.sameAdddress(lb.computeStateForProgram(program)); } - return en; + return state; } - default Enablement computeEnablement(Collection col, Trace trace) { - Enablement en = Enablement.NONE; + default State computeState(Collection col, Trace trace) { + State state = State.NONE; for (LogicalBreakpoint lb : col) { - en = en.sameAdddress(lb.computeEnablementForTrace(trace)); + state = state.sameAdddress(lb.computeStateForTrace(trace)); } - return en; + return state; } - default Enablement computeEnablement(Collection col, ProgramLocation loc) { + default State computeState(Collection col, ProgramLocation loc) { return programOrTrace(loc, - (p, a) -> computeEnablement(col, p), - (t, a) -> computeEnablement(col, t)); + (p, a) -> computeState(col, p), + (t, a) -> computeState(col, t)); } - default Enablement computeEnablement(ProgramLocation loc) { + /** + * Compute the state for a given address and program or trace view + * + * @param loc the location + * @return the breakpoint state + */ + default State computeState(ProgramLocation loc) { Set col = getBreakpointsAt(loc); - return computeEnablement(col, loc); + return computeState(col, loc); } default boolean anyMapped(Collection col, Trace trace) { @@ -222,11 +240,11 @@ public interface DebuggerLogicalBreakpointService { Collection kinds, String name); /** - * Create an enabled breakpoint at the given trace location only. + * Create an enabled breakpoint at the given trace location and its mapped program location. * *

- * If the given location is mapped to a static module, this still only creates the breakpoint in - * the given trace. However, a logical breakpoint mark will appear at all mapped locations. + * If the breakpoint has no static location, then only the trace location is placed. Note, if + * this is the case, the breakpoint will have no name. * *

* Note, the debugger ultimately determines the placement behavior. If it is managing multiple @@ -238,10 +256,11 @@ public interface DebuggerLogicalBreakpointService { * @param address the address in the trace (as viewed in the present) * @param length size of the breakpoint, may be ignored by debugger * @param kinds the kinds of breakpoint + * @param name a name for the breakpoint * @return a future which completes when the breakpoint has been placed */ CompletableFuture placeBreakpointAt(Trace trace, Address address, long length, - Collection kinds); + Collection kinds, String name); /** * Create an enabled breakpoint at the given location. diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/LogicalBreakpoint.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/LogicalBreakpoint.java index 7d90ff9dcf..4a506dd361 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/LogicalBreakpoint.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/LogicalBreakpoint.java @@ -15,10 +15,12 @@ */ package ghidra.app.services; -import java.util.Collection; -import java.util.Set; +import java.util.*; import java.util.concurrent.CompletableFuture; +import javax.swing.Icon; + +import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.framework.model.DomainObject; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Bookmark; @@ -35,24 +37,24 @@ public interface LogicalBreakpoint { /** * The state of a logical breakpoint's program bookmark */ - public enum ProgramEnablement { + public enum ProgramMode { /** * A placeholder state when the program bookmark state is not applicable */ NONE { @Override - public Enablement combineTrace(TraceEnablement traceEn) { - switch (traceEn) { + public State combineTrace(TraceMode traceMode, Perspective perspective) { + switch (traceMode) { case NONE: - return Enablement.NONE; + return State.NONE; case MISSING: - return Enablement.NONE; + return State.NONE; case ENABLED: - return Enablement.ENABLED; - case MIXED: - return Enablement.DISABLED_ENABLED; + return State.INCONSISTENT_ENABLED; case DISABLED: - return Enablement.DISABLED; + return State.INCONSISTENT_DISABLED; + case MIXED: + return State.INCONSISTENT_MIXED; default: throw new AssertionError(); } @@ -67,17 +69,18 @@ public interface LogicalBreakpoint { */ MISSING { @Override - public Enablement combineTrace(TraceEnablement traceEn) { - switch (traceEn) { + public State combineTrace(TraceMode traceMode, Perspective perspective) { + switch (traceMode) { case NONE: - return Enablement.NONE; + return State.NONE; case MISSING: - return Enablement.NONE; + return State.NONE; case ENABLED: - case MIXED: - return Enablement.DISABLED_ENABLED; + return State.INCONSISTENT_ENABLED; case DISABLED: - return Enablement.DISABLED; + return State.INCONSISTENT_DISABLED; + case MIXED: + return State.INCONSISTENT_MIXED; default: throw new AssertionError(); } @@ -88,16 +91,32 @@ public interface LogicalBreakpoint { */ ENABLED { @Override - public Enablement combineTrace(TraceEnablement traceEn) { - switch (traceEn) { + public State combineTrace(TraceMode traceMode, Perspective perspective) { + switch (traceMode) { case NONE: case MISSING: - return Enablement.INEFFECTIVE_ENABLED; + switch (perspective) { + case LOGICAL: + return State.INEFFECTIVE_ENABLED; + case TRACE: + return State.NONE; + } case ENABLED: - return Enablement.ENABLED; + return State.ENABLED; case DISABLED: + switch (perspective) { + case LOGICAL: + return State.INCONSISTENT_ENABLED; + case TRACE: + return State.INCONSISTENT_DISABLED; + } case MIXED: - return Enablement.ENABLED_DISABLED; + switch (perspective) { + case LOGICAL: + return State.INCONSISTENT_ENABLED; + case TRACE: + return State.INCONSISTENT_MIXED; + } default: throw new AssertionError(); } @@ -108,16 +127,33 @@ public interface LogicalBreakpoint { */ DISABLED { @Override - public Enablement combineTrace(TraceEnablement traceEn) { - switch (traceEn) { + public State combineTrace(TraceMode traceMode, Perspective perspective) { + switch (traceMode) { case NONE: case MISSING: - return Enablement.INEFFECTIVE_DISABLED; + switch (perspective) { + case LOGICAL: + return State.INEFFECTIVE_DISABLED; + case TRACE: + return State.NONE; + } case ENABLED: - case MIXED: - return Enablement.DISABLED_ENABLED; + switch (perspective) { + case LOGICAL: + return State.INCONSISTENT_DISABLED; + case TRACE: + return State.INCONSISTENT_ENABLED; + } case DISABLED: - return Enablement.DISABLED; + return State.DISABLED; + case MIXED: + switch (perspective) { + case LOGICAL: + return State.INCONSISTENT_DISABLED; + case TRACE: + return State.INCONSISTENT_MIXED; + } + return State.INCONSISTENT_MIXED; default: throw new AssertionError(); } @@ -132,183 +168,209 @@ public interface LogicalBreakpoint { * This state is generally considered the state of the logical breakpoint. In other words, * the program's perspective is the default. * - * @param traceEn the state of its locations + * @param traceMode the mode of its locations + * @param perspective the perspective * @return the logical state */ - public abstract Enablement combineTrace(TraceEnablement traceEn); + public abstract State combineTrace(TraceMode traceMode, Perspective perspective); + } + + public enum Perspective { + LOGICAL, TRACE; } /** * The state of a logical breakpoint's trace/target locations */ - public enum TraceEnablement { + public enum TraceMode { /** - * A placeholder state when the breakpoint is not mapped to any trace + * A placeholder mode when no traces are involved */ NONE { @Override - public TraceEnablement combine(TraceEnablement that) { + public TraceMode combine(TraceMode that) { return that; } - - @Override - public Enablement combineProgram(ProgramEnablement progEn) { - switch (progEn) { - case NONE: - case MISSING: - return Enablement.NONE; - case ENABLED: - return Enablement.INEFFECTIVE_ENABLED; - case DISABLED: - return Enablement.INEFFECTIVE_DISABLED; - default: - throw new AssertionError(); - } - } }, /** - * The state when the breakpoint is mapped to at least one trace, but no locations are - * placed + * The mode when the breakpoint is missing from one or more of its mapped locations */ MISSING { @Override - public TraceEnablement combine(TraceEnablement that) { - return that; - } - - @Override - public Enablement combineProgram(ProgramEnablement progEn) { - switch (progEn) { - case NONE: - case MISSING: - return Enablement.NONE; - case ENABLED: - return Enablement.INEFFECTIVE_ENABLED; - case DISABLED: - return Enablement.INEFFECTIVE_DISABLED; - default: - throw new AssertionError(); - } + public TraceMode combine(TraceMode that) { + return MISSING; } }, /** - * The state when all mapped locations are placed and enabled + * The mode when all mapped locations are placed and enabled */ ENABLED { @Override - public TraceEnablement combine(TraceEnablement that) { + public TraceMode combine(TraceMode that) { switch (that) { case NONE: - case MISSING: case ENABLED: return ENABLED; case DISABLED: case MIXED: return MIXED; - default: - throw new AssertionError(); - } - } - - @Override - public Enablement combineProgram(ProgramEnablement progEn) { - switch (progEn) { - case NONE: case MISSING: - case DISABLED: - return Enablement.ENABLED_DISABLED; - case ENABLED: - return Enablement.ENABLED; + return MISSING; default: throw new AssertionError(); } } }, /** - * The state when all mapped locations are placed and disabled + * The mode when all mapped locations are placed and disabled */ DISABLED { @Override - public TraceEnablement combine(TraceEnablement that) { + public TraceMode combine(TraceMode that) { switch (that) { case NONE: - case MISSING: case DISABLED: return DISABLED; case ENABLED: case MIXED: return MIXED; - default: - throw new AssertionError(); - } - } - - @Override - public Enablement combineProgram(ProgramEnablement progEn) { - switch (progEn) { - case NONE: case MISSING: - case DISABLED: - return Enablement.DISABLED; - case ENABLED: - return Enablement.DISABLED_ENABLED; + return MISSING; default: throw new AssertionError(); } } }, /** - * The state when some mapped locations are enabled and some are disabled + * The mode when all mapped locations are placed, but some are enabled, and some are + * disabled */ MIXED { @Override - public TraceEnablement combine(TraceEnablement that) { - return MIXED; - } - - @Override - public Enablement combineProgram(ProgramEnablement progEn) { - return Enablement.ENABLED_DISABLED; + public TraceMode combine(TraceMode that) { + switch (that) { + case NONE: + case ENABLED: + case DISABLED: + case MIXED: + return MIXED; + case MISSING: + return MISSING; + default: + throw new AssertionError(); + } } }; /** - * Convert a boolean to trace enablement + * Convert a boolean to trace breakpoint mode * - * @param en true for {@link #ENABLED}, false for {@link #DISABLED} + * @param enabled true for {@link #ENABLED}, false for {@link #DISABLED} * @return the state */ - public static TraceEnablement fromBool(boolean en) { - return en ? ENABLED : DISABLED; + public static TraceMode fromBool(boolean enabled) { + return enabled ? ENABLED : DISABLED; } /** - * For locations of the same logical breakpoint, compose the state + * For locations of the same logical breakpoint, compose the mode * * @param that the other state * @return the composed state */ - public abstract TraceEnablement combine(TraceEnablement that); - - /** - * Compose the logical breakpoint state from the perspective of the trace, given the state - * of its program bookmark. - * - *

- * Typically, this is used not on a composed trace state, but on the one trace whose - * perspective to consider. This should only be used when choosing how to render a - * breakpoint in that trace's listing. - * - * @param progEn the state of the program bookmark - * @return the per-trace logical state - */ - public abstract Enablement combineProgram(ProgramEnablement progEn); + public abstract TraceMode combine(TraceMode that); } /** - * The state of a logical breakpoint, i.e., whether or not its parts are enabled + * The mode of a logical breakpoint + * + *

+ * Depending on context this may describe the mode from the perspective of a program, where + * breakpoints are saved from session to session; or this may describe the mode from the + * perspective of one or more traces/targets: + * + *

+ * If the breakpoint is a lone breakpoint, meaning Ghidra cannot determine to what program it + * belongs, then this describes the mode of that trace breakpoint. + * + *

+ * If the breakpoint is mapped, meaning Ghidra can determine to what program it belongs and at + * what address, but it is not bookmarked, then for the static context, this describes the mode + * of the participating trace breakpoints. If the breakpoint is bookmarked, then for the static + * context, this describes the mode of that bookmark. For the dynamic context, this describes + * the mode of the trace's breakpoint, ignoring the presence or state of the bookmark. Note that + * the bookmark and trace modes may disagree. The displayed mode is still determined by context, + * but it will be marked as inconsistent. See {@link Consistency}. */ - public enum Enablement { + public enum Mode { + /** All locations are enabled */ + ENABLED, + /** All locations are disabled */ + DISABLED, + /** Has both enabled and disabled trace locations */ + MIXED; + + public Mode sameAddress(Mode that) { + if (this == Objects.requireNonNull(that)) { + return this; + } + return MIXED; + } + } + + /** + * The consistency of a logical breakpoint + * + *

+ * When operating as designed, all breakpoints should be in the {@link #NORMAL} state. This + * indicates that the breakpoint's bookmark and all trace locations agree on the mode. + * Exceptions do happen, and they should be indicated to the user: + * + *

+ * If the breakpoint is a lone breakpoint, meaning Ghidra cannot determine to what program it + * belongs, then the breakpoint is always {@link #INCONSISTENT}, because Ghidra uses program + * bookmarks to save breakpoints. + * + *

+ * If the breakpoint is mapped, meaning Ghidra can determine to what program it belongs and at + * what address, but it is not bookmarked, then the breakpoint is {@link #INCONSISTENT}. If it + * is bookmarked, but the bookmark disagrees, then the breakpoint is {@link #INCONSISTENT}. A + * breakpoint that is bookmarked but has no trace locations, or is missing from any + * participating trace, is {@link #INEFFECTIVE}. + * + * @implNote These are ordered by priority, highest last. + */ + public enum Consistency { + /** the bookmark and locations all agree */ + NORMAL, + /** has a bookmark but one or more trace locations is missing */ + INEFFECTIVE, + /** has a trace location but is not bookmarked, or the bookmark disagrees */ + INCONSISTENT; + + public Consistency sameAddress(Consistency that) { + return Consistency.values()[Math.max(this.ordinal(), that.ordinal())]; + } + } + + /** + * The state of a logical breakpoint + * + *

+ * Because a breakpoint is comprised of possibly many locations on target or among several + * targets, as well as a saved bookmark in a program, the "state" can get fairly complex. This + * is an attempt to enumerate these states while preserving enough information about the + * breakpoint to display it in various contexts, hopefully informing more than confusing. + * + *

+ * In essence, this is the cross product of {@link Mode} and {@link Consistency} with an + * additional {@link #NONE} option. + * + *

+ * A breakpoint is simply {@link #ENABLED} or {@link #DISABLED} if it is maped and all its + * locations and bookmark agree. Ideally, all breakpoints would be in one of these two states. + */ + public enum State { /** * A placeholder state, usually indicating the logical breakpoint should not exist * @@ -317,77 +379,95 @@ public interface LogicalBreakpoint { * breakpoint is ephemeral and about to be removed. This value may appear during * computations and is a suitable default placeholder for editors and renderers. */ - NONE(false, false, true, false) { - @Override - public Enablement getPrimary() { + NONE(null, null, null, null), + /** + * The breakpoint is enabled, and all locations and its bookmark agree + */ + ENABLED(Mode.ENABLED, Consistency.NORMAL, DebuggerResources.NAME_BREAKPOINT_MARKER_ENABLED, DebuggerResources.ICON_BREAKPOINT_MARKER_ENABLED), + /** + * The breakpoint is disabled, and all locations and its bookmark agree + */ + DISABLED(Mode.DISABLED, Consistency.NORMAL, DebuggerResources.NAME_BREAKPOINT_MARKER_DISABLED, DebuggerResources.ICON_BREAKPOINT_MARKER_DISABLED), + /** + * There are multiple logical breakpoints at this address, and they are all saved and + * effective, but some are enabled, and some are disabled. + */ + MIXED(Mode.MIXED, Consistency.NORMAL, DebuggerResources.NAME_BREAKPOINT_MARKER_MIXED, DebuggerResources.ICON_BREAKPOINT_MARKER_MIXED), + /** + * The breakpoint is saved as enabled, but one or more trace locations are absent. + */ + INEFFECTIVE_ENABLED(Mode.ENABLED, Consistency.INEFFECTIVE, DebuggerResources.NAME_BREAKPOINT_MARKER_INEFF_EN, DebuggerResources.ICON_BREAKPOINT_MARKER_INEFF_EN), + /** + * The breakpoint is saved as disabled, and one or more trace locations are absent. + */ + INEFFECTIVE_DISABLED(Mode.DISABLED, Consistency.INEFFECTIVE, DebuggerResources.NAME_BREAKPOINT_MARKER_INEFF_DIS, DebuggerResources.ICON_BREAKPOINT_MARKER_INEFF_DIS), + /** + * There are multiple logical breakpoints at this address, and they are all saved, but at + * least one is ineffective; furthermore, some are enabled, and some are disabled. + */ + INEFFECTIVE_MIXED(Mode.MIXED, Consistency.INEFFECTIVE, DebuggerResources.NAME_BREAKPOINT_MARKER_INEFF_MIX, DebuggerResources.ICON_BREAKPOINT_MARKER_INEFF_MIX), + /** + * The breakpoint is enabled, and all locations agree, but the bookmark is absent or + * disagrees. + */ + INCONSISTENT_ENABLED(Mode.ENABLED, Consistency.INCONSISTENT, DebuggerResources.NAME_BREAKPOINT_MARKER_INCON_EN, DebuggerResources.ICON_BREAKPOINT_MARKER_INCON_EN), + /** + * The breakpoint is disabled, and all locations agree, but the bookmark is absent or + * disagrees. + */ + INCONSISTENT_DISABLED(Mode.DISABLED, Consistency.INCONSISTENT, DebuggerResources.NAME_BREAKPOINT_MARKER_INCON_DIS, DebuggerResources.ICON_BREAKPOINT_MARKER_INCON_DIS), + /** + * The breakpoint is terribly inconsistent: its locations disagree, and the bookmark may be + * absent. + */ + INCONSISTENT_MIXED(Mode.MIXED, Consistency.INCONSISTENT, DebuggerResources.NAME_BREAKPOINT_MARKER_INCON_MIX, DebuggerResources.ICON_BREAKPOINT_MARKER_INCON_MIX); + + public final Mode mode; + public final Consistency consistency; + public final String display; + public final Icon icon; + + State(Mode mode, Consistency consistency, String display, Icon icon) { + this.mode = mode; + this.consistency = consistency; + this.display = display; + this.icon = icon; + } + + public static State fromFields(Mode mode, Consistency consistency) { + if (mode == null && consistency == null) { return NONE; } - }, - /** - * The breakpoint's bookmark and all mapped locations are placed and enabled - */ - ENABLED(true, false, true, true) { - @Override - public Enablement getPrimary() { - return ENABLED; + switch (mode) { + case ENABLED: + switch (consistency) { + case NORMAL: + return ENABLED; + case INEFFECTIVE: + return INEFFECTIVE_ENABLED; + case INCONSISTENT: + return INCONSISTENT_ENABLED; + } + case DISABLED: + switch (consistency) { + case NORMAL: + return DISABLED; + case INEFFECTIVE: + return INEFFECTIVE_DISABLED; + case INCONSISTENT: + return INCONSISTENT_DISABLED; + } + case MIXED: + switch (consistency) { + case NORMAL: + return MIXED; + case INEFFECTIVE: + return INEFFECTIVE_MIXED; + case INCONSISTENT: + return INCONSISTENT_MIXED; + } } - }, - /** - * The breakpoint's bookmark and all mapped locations are placed and disabled - */ - DISABLED(false, true, true, true) { - @Override - public Enablement getPrimary() { - return DISABLED; - } - }, - /** - * The breakpoint's bookmark is enabled, but it has no mapped locations placed - */ - INEFFECTIVE_ENABLED(true, false, true, false) { - @Override - public Enablement getPrimary() { - return ENABLED; - } - }, - /** - * The breakpoint's bookmark is disabled, and it has no mapped locations placed - */ - INEFFECTIVE_DISABLED(false, true, true, false) { - @Override - public Enablement getPrimary() { - return DISABLED; - } - }, - /** - * The breakpoint's bookmark is enabled, but at least one mapped location is disabled - */ - ENABLED_DISABLED(true, false, false, true) { - @Override - public Enablement getPrimary() { - return ENABLED; - } - }, - /** - * The breakpoint's bookmark is disabled, but at least one mapped location is enabled - */ - DISABLED_ENABLED(false, true, false, true) { - @Override - public Enablement getPrimary() { - return DISABLED; - } - }; - - public final boolean enabled; // indicates any enabled location - public final boolean disabled; // indicates any disabled location - public final boolean consistent; // bookmark and target locations all agree - public final boolean effective; // has a target location, even if disabled - - Enablement(boolean enabled, boolean disabled, boolean consistent, boolean effective) { - this.enabled = enabled; - this.disabled = disabled; - this.consistent = consistent; - this.effective = effective; + throw new AssertionError(); } /** @@ -401,65 +481,33 @@ public interface LogicalBreakpoint { * @param that the other state. * @return the composed state */ - public Enablement sameAdddress(Enablement that) { + public State sameAdddress(State that) { if (this == NONE) { return that; } if (that == NONE) { return this; } - if (!this.effective && !that.effective) { - return this.enabled || that.enabled ? INEFFECTIVE_ENABLED : INEFFECTIVE_DISABLED; - } - return fromBools(this.enabled || that.enabled, this.enabled && that.enabled); - } + Mode mode = this.mode.sameAddress(that.mode); + Consistency consistency = this.consistency.sameAddress(that.consistency); + return fromFields(mode, consistency); + }; /** * For logical breakpoints which appear at the same address, compose their state * - * @see #sameAdddress(Enablement) + * @see #sameAdddress(State) * @param col a collection of states derived from logical breakpoints at the same address * @return the composed state */ - public static Enablement sameAddress(Collection col) { - Enablement result = NONE; - for (Enablement e : col) { - result = result.sameAdddress(e); + public static State sameAddress(Collection col) { + State result = NONE; + for (State state : col) { + result = result.sameAdddress(state); } return result; } - public static Enablement fromBools(boolean firstEn, boolean secondEn) { - if (firstEn) { - if (secondEn) { - return ENABLED; - } - else { - return ENABLED_DISABLED; - } - } - else { - if (secondEn) { - return DISABLED_ENABLED; - } - else { - return DISABLED; - } - } - } - - /** - * Get the "primary" state represented by this logical state - * - *

- * Generally, this is the state of the bookmark, i.e., program breakpoint. Not all logical - * breakpoints have a placed bookmark, though. In that case, this still returns a reasonable - * "primary" state. - * - * @return the state - */ - public abstract Enablement getPrimary(); - /** * Get the desired state were the the logical breakpoint to be toggled * @@ -479,11 +527,31 @@ public interface LogicalBreakpoint { * @param mapped true if the breakpoint is mapped, as interpreted by the action context * @return the resulting state */ - public Enablement getToggled(boolean mapped) { - // If not mapped, just toggle. If mapped, consider any other state "disabled" - // This will cause most first toggles to make it consistent enabled-effective. - boolean en = mapped ? this == ENABLED : enabled; - return en ? DISABLED : ENABLED; // The actual toggle (and type conversion) + public State getToggled(boolean mapped) { + if (mapped && isIneffective()) { + return ENABLED; + } + return isDisabled() ? ENABLED : DISABLED; + } + + public boolean isNormal() { + return consistency == Consistency.NORMAL; + } + + public boolean isEnabled() { + return mode != Mode.DISABLED; // mixed is considered, in part, enabled + } + + boolean isDisabled() { + return mode != Mode.ENABLED; // mixed is considered, in part, disabled + } + + public boolean isEffective() { + return consistency != Consistency.INEFFECTIVE; + } + + public boolean isIneffective() { + return consistency == Consistency.INEFFECTIVE; } } @@ -584,7 +652,7 @@ public interface LogicalBreakpoint { * trace, but rather that for each returned trace, the logical breakpoint can be mapped to an * address in that trace. See {@link #getParticipatingTraces()}. * - * @return the set of traces + * @return a copy of the set of traces */ Set getMappedTraces(); @@ -622,27 +690,38 @@ public interface LogicalBreakpoint { Address getAddress(); /** - * Compute the enablement status for the given program. + * Compute the state for the given program. * * @param program the program - * @return the enablement + * @return the state */ - Enablement computeEnablementForProgram(Program program); + State computeStateForProgram(Program program); /** - * Compute the enablement status for the given trace. + * Compute the state for the given trace. * * @param trace the trace - * @return the enablement + * @return the state */ - Enablement computeEnablementForTrace(Trace trace); + State computeStateForTrace(Trace trace); /** - * Compute the enablement status for all involved traces and program. + * Compute the state for the given location. * - * @return the enablement + *

+ * This is just the location's mode combined with that of the static bookmark. + * + * @param loc the location + * @return the state */ - Enablement computeEnablement(); + State computeStateForLocation(TraceBreakpoint loc); + + /** + * Compute the state for all involved traces and program. + * + * @return the state + */ + State computeState(); /** * Place an "enabled breakpoint" bookmark in the mapped program, if applicable. diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-clear.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-clear.png deleted file mode 100644 index bb783d7247459c3ab38cb4144f7c3c265e398d88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1449 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`vr;2G(|mmyw18|51~x_^24*0O z5eOL=r5ISjYzBrfMrk-Zj!^@uh6$*~kO4?Chyrn1dnN-5RDBJQ27v@12ATw+12QX8 zb8-_)@{1G#it-hV^bGZkfO1R=nBe*Z7BC~&Ksg5QD?%@U6lZ})WHAE+-w_aIoT|+y z&A`B%o*5ER65;D(m7Jfemza{Dl&V*eTL99lF>KRGtkGT4d_|8`%rXMBo^S*RFav7 z!!D2vL>o9TAa1k)xxfk>iIFauxv6=@K>yeon%RJzh9QCQ9FoQeTpE!ikTe3lW#wFy z3Q95|`T03^*iw*S0sE$sqkWhYM; z#}Es_(XN z^~cYzW;*J?rrWwQy~Xf>iOXkJg?J8~Z_mvS|4;UojCeOc;lZE(>9?Q9&;Q%_s6OG< z%mf|@-mQ{LDqsKqF8^d^xzCLQU!E8pmahzM(-#!p&v^9VEXOJX9)_-+jVk}@E9aiw zV72$}`OU_RJ~oSPORPzjl(5)wph%%o;(!Umi}W`8{Spy;Ix`qTL|Bhx|GS~~)L_#l zrkOnso*@mH2j0*B{ZBsn+=us(OP5rp5+FMsHlM@(H=Jo?*HZ zB)#v?&v(q9j8EJQnC-ag-oO9HlMWcD|KQvtRwAzR>%-TX{LlaIkCxC?y2fBy&lL5W wGvl*X&a8>NO&bq4@C3*t`^y`I9`0siP+pdDGDA3^0#rPx# diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-disable.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-disable.png deleted file mode 100755 index 344e4a2e2473c8ddf20a749334ebe5334fd12d4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1355 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`vr;2G(|mmyw18|51~x_^24*0O z5eOL=r5ISjYzBrfMrk-Zj!^@uh6$*~kO4?Chyrn1dnN-5RDBJQ27v@12ATw+12QX8 zb8-_)@{1G#it-hV^bGZkfO1R=nBe*Z7BC~&Ksg5QD?%@U6lZ})WHAE+-w_aIoT|+y z&A`B%o*5ER65;D(m7Jfemza{Dl&V*eTL99lF>KRGtkGT4d_|8`%rXMBo^S*RFav7 z!!D2vL>o9TAa1k)xxfk>iIFauxv6=@K>yeon%RJzh9QCQ9FoQeTpE!ikTe3lW#wFy z3Q95|`T03^*iw*OE_wNVDdcreZ#YUhSwf7%v$_BKc;5NgPVKA5A0Y=*Y^)kCa)MD%R-s`hNLq+xAp8e#gRIpO7D| z2afJZo@Oiac}~RPsTG}PQV(p6l-+K8GLl(WAn~P<3r9#UmxcOov6C^|4ZfXnF8Cbf zC1Mu6)$^xT=FVdQ&MBb@02lF>KRGtkGT4d_|8`%rXMBo^S*RFav7 z!!D2vL>o9TAa1k)xxfk>iIFauxv6=@K>yeon%RJzh9QCQ9FoQeTpE!ikTe3lW#wFy z3Q95|`T03^*iw*P=I$)pU-S6e(? z978MwlM^IZdm@?@3U}%#YX{p0oa5En|J~j=nf36O4YOaoGURQ@*vAl}(Xwd9#D{V*131iRyEhQxhZ@_;M2(7HYG6y8rL@>7X9HzC;F%RSpZ! zp7>C2%B*}?U4ZS@o>%|v#hBYH5Bgu=m3V4wz`(G)RU;zkiHj1bVDNPHb6Mw<&;$T+ C(0?)j diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-ineffective-d.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-ineffective-d.png deleted file mode 100644 index cb1d3782358eedf419204ceae8ff7336febee233..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 353 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1U#L=PLN^!uU@$m4`C0EXW}5@iOSYPsAFE+_!Pu~BT~g2HO*-al*{4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1T6d%8G= zSoFT_Q{+43AmWmK;~nFQW$6zXzCPH=wd~}qt}vmzMt7yGDkdGNoM7KsXA=|}moBZS z`1JJoU&enM{H>k~Wxo|Wz{H>udhhodjjqcX2P~(%1eUM}?3bC8d%g7YCY|%=>x9_e z&3}BsdsbAo`NDc`o3!tWnLm9K1SU!GdaSb3vFKsj^Ih`fw&&H(MY2`GUAxxJHt{V# sHbb(3<$&mcc~7*?e?EO-SNt=!bRLtK9d{-z13H_*)78&qol`;+09f;Jj{pDw diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-mixed-de.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoint-mixed-de.png deleted file mode 100755 index 56823827c29227e88c386042836d43828d921604..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1439 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 FngEVV;eG%B diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-enable-all.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-enable-all.png deleted file mode 100755 index 6e5e936d4ceb0bab487bce08ea8821175754f1ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1406 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`vr;2G(|mmyw18|51~x_^24*0O z5eOL=r5ISjYzBrfMrk-Zj!^@uh6$*~kO4?Chyrn1dnN-5RDBJQ27v@12ATw+12QX8 zb8-_)@{1G#it-hV^bGZkfO1R=nBe*Z7BC~&Ksg5QD?%@U6lZ})WHAE+-w_aIoT|+y z&A`B%o*5ER65;D(m7Jfemza{Dl&V*eTL99lF>KRGtkGT4d_|8`%rXMBo^S*RFav7 z!!D2vL>o9TAa1k)xxfk>iIFauxv6=@K>yeon%RJzh9QCQ9FoQeTpE!ikTe3lW#wFy z3Q95|`T03^*iw*TClvls%ivbd*< zV~B;|(%`*Zha7nJUHQeZ%i8)?u*MEC7kBZ4kpl5fGOU6!M_;YuJGXA#xm{uh13x5P z`gSX>zFlsL+WqM@)4kudXzWxtA^%n?z;xP^@|rhYqBFWf{|g>jA~COFdX4(Nbw?X2 z9*1u`QugD@hmPb8Z{M#`H*Q>%7Ls@{?OG9QM@XvrBY_388I&7@IvfmzSN6?W%Cauf zkm1p!qlWKH3zN(pAN8J>;V!$-AXjL7=&Iq?ml?@V+EyA17fxf~_Oc7ybnn555~s!+ z*WMhes9!RFs#D<0iV3eBJqQk?IBGxgTe~DWM4f DDyg%m diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-make-effective.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints-make-effective.png deleted file mode 100755 index 60d9bbe7a1bfc562d7472a12b3f82a306769d041..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1441 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`vr;2G(|mmyw18|51~x_^24*0O z5eOL=r5ISjYzBrfMrk-Zj!^@uh6$*~kO4?Chyrn1dnN-5RDBJQ27v@12ATw+12QX8 zb8-_)@{1G#it-hV^bGZkfO1R=nBe*Z7BC~&Ksg5QD?%@U6lZ})WHAE+-w_aIoT|+y z&A`B%o*5ER65;D(m7Jfemza{Dl&V*eTL99lF>KRGtkGT4d_|8`%rXMBo^S*RFav7 z!!D2vL>o9TAa1k)xxfk>iIFauxv6=@K>yeon%RJzh9QCQ9FoQeTpE!ikTe3lW#wFy z3Q95|`T03^*iw*PR}56Z@jR-P`7 zAr^v512_5|cHr6i@+tGFAh92}}@^8jhTb zxwcA3Iq=b~JpH0sCZR_?1Lxhpd46AdL%rOWuZa?JY>ozxez;gD%}R+h>sj>T_VX)$ zLmj&27RS%okUv?VaNe>^^S;L`pX%P5oN#q#h>nf_#b>)+J}g_zRI@Vqy6VOgnX>OK z^&dPwHGjUN=p_Lyg*M4`EI;3Eby>semsB|0_f28Z?R&-FBcH!jZam$tUUu8Q*hyFB z&{>D=oA(`Sas4wpp6{OhBhNoH=*+lwOocv0lkFJ7X62Fmm3`?1Q~bo3sj}d6-+` ofA07^qo|yx{O^3Jhj&-92Baj%oIF}B2`ZjEUHx3vIVCg!0KwYIp8x;= diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints.png b/Ghidra/Debug/Debugger/src/main/resources/images/alt-breakpoints.png deleted file mode 100755 index 2bdaccc11b5abab88459641b14a3fcbcc071b608..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1445 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`vr;2G(|mmyw18|51~x_^24*0O z5eOL=r5ISjYzBrfMrk-Zj!^@uh6$*~kO4?Chyrn1dnN-5RDBJQ27v@12ATw+12QX8 zb8-_)@{1G#it-hV^bGZkfO1R=nBe*Z7BC~&Ksg5QD?%@U6lZ})WHAE+-w_aIoT|+y z&A`B%o*5ER65;D(m7Jfemza{Dl&V*eTL99lF>KRGtkGT4d_|8`%rXMBo^S*RFav7 z!!D2vL>o9TAa1k)xxfk>iIFauxv6=@K>yeon%RJzh9QCQ9FoQeTpE!ikTe3lW#wFy z3Q95|`T03^*iw*P2Pc&`FxWjjw7 z#}Es_rNJA$m>mUbH^Y;^W^Ty(AJhp^((B93WESArI~W<^J5Z{=x9 zbt;>)^S15xqHMm84{ppf|9tP~m8oP}HN_RW*zZ(Pv2z2xizMTyhPT=y#MPkYAH^emP8BZv4) zc0KF&Gd?X$GrkzsE6Nb3(D2MhGnH@Uj?T|+o8BGpF_yVgZa8~_jP_*-!FghuBEB|q zYbHKTx7{rK<#a^VCf$v}jEgR>>&~l6;wody85}Sb4q9e0D&CG@Bjb+ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-clear.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-clear.png index e031698377c6ec00774e3c0ffca07964f0c2ce07..85735d19c2c6673243f58715aec251608816aec3 100644 GIT binary patch delta 567 zcmV-70?7UC0+|JnZGQq5Nkl1wk0uj764Q(%?1GhlJ0=k&sz|x8n z{0B&a=?aq&C;tP%&9z1?%r4>rZ3jbA6Ig&nvE1qaa$CZ$Ye*12hjMFgVep-9&OOh2 z-t(SwglnX#?Z7b51t{77gl3-#3VbNAUmC?0Y2=H+Yytg`88|X z^&d^QE*okuF$v#H^Xr%U`*)@9{qMaBpnT?iZ1A zGk}P=s+t6HQ}b)q(dpTh(wvG%xUI#9lOj^CmRToNjcvQ$Yk>Fcj!0Qzo+ z7`fMZo-i8fc^8kG&wcocL7h>U>T|X)hf2V*h#WS~{R=#p`^GGu+^hfq002ovPDHLk FV1jVC7Wn`G delta 265 zcmV+k0rvix1?>WmZGQmjNkl@3C(7^~Uo%#TRuiz?T0>wcahbEvgDvo-_%YQD1b1#2h;V;>9%;T;Y z&Q_53$>nlu-}i3~!}ug3^N5ihxZpU> zeYe|9g<*Io7_nH4dc8g`l}cA4^6)o-QtHBSocm6vlLE-+^OVbFve_&E)9IA)c+6xn zq0{N4fjgyCK}6;P;QRjfcDvmS!;o67Mx|0Ym|!#-F>cb)8nLbz>ODM@=bZ*tUHg zh9UWU{wNG^d3lLxnygkUY};-srOq{=>bkB5P%f8`!qG$;*LBZ;ss_9df&hR_CUept zdKcgK0X}GK{`tR0sf}Dj0M;7tm`Ef5n9XJ<4I+(U7yyqN(9h*^Yk={1e9|D&n5Ov( z3^Wm0T9$Pii^Z5sCX7a-qwsJz#C2VCUB|Ml8xeVt?Y+{D<2-Cjf@zuui{!d40F6fD zsaPx)MC5tD%6Gk9FTGl=4kplboqE0gR4f*+MC2#z?84S0wOlU0*tXsDJnw98nO-f+ z`WBDJKa0q7#QbBhV|31Do!@PbegSfxp0jh^!y4O+5zY1Xj1+Jp1Z*dVeQ^#-C6W07s66y3)&g zbD^lG*p~T7M8f-w9D++cK6CiNr(fBT*iN!wSkhDJyjKx!sla{mZ8Be5MC%kz11=W5dU08j5;qoFL1&Db{XkwBBGS`DD4 z*B>+h+G`yr!u#q-)Oh{NDL{<@6wHU?-L6Rd`7~A9F90wXjsO%IfQp=k9F?K~NPK{M z2C!IcI|sm+xpt<(*y29`63n6j4Az(C?E$#@XU{Zn4S&o4P+yk23w$?3Wc_}1p~sS* zO3(NbFZ-rXgr9q-==BG&W~9+lUG!c=HUvObZJtqcvf1sksX{|p9v!s~ZWhEFHoCBe zYhZ?6e-IJqYPl71R@qBLWcBDOkD5adKmN9D#AVqg##ea?4&6e+Bzqye z_1ZlWXhZ6oSWcN;i2&fZWTEbQ?ruwU(T9SInU6(eb)QK(KvlI8KWBU5$pi*PWaD`4 Yf0@qou_T;JJ^%m!07*qoM6N<$f;I9S0ssI2 delta 194 zcmV;z06qV#1*Za#ZGQlyNkl$-i# za2y9~Z8@LM7b5cgPXkpw$+GM&48!qWv#0Ai!Z4fwx2k$7BBcSS>-sv1qJPTU#Bn@n zn&uL~sH&Hy>Df!Q_tCa3X_}s^>ahWqd7hhB+dX-n9|21Pyszu}P@k%*06rL=-}zuf z1Ym7|`{8hSaDeA|0QUw+eBWOm8ZezspMY;hM4BK7ZXCyXwLBOM2!i0Vh&-CU&V{wM z{GYhNTKnL-?kDJfqKK5Lx&UtDIG(g^`{K>k+K1U}wh)nDu(b;Ssyc3(<|<9o^P(t@ zc9!WW2!b!J>wetGe+6s>9rL`-@A{)3Kq4ZKyR*Lm9sr&h>jV?&00000NkvXXu0mjf D^0C|$ literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-enable.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-enable.png index ea1e9268110c56bf44d5a66528f777d7e405e2c8..2d618a99a741400e70a6c5da2cc51a2d381d70e2 100644 GIT binary patch delta 462 zcmV;<0WtoK0;ZGQo-Nklh~YVKQv` zAAFM7xaw%yrT>9-WTk0?&RuF3e+`WZgD%*mwV#p>S}ubMBqS=w0Z>9etGwgQ`@Hwf zz2^u%Qq^7{0lWkh_ycT+$XUBL&;~}mVrAw1mz=wPaAJNH%6|ZiL_8%tlmwSpZ7a zE9TN!2iR8CQ4z5Qz$;c(7S{Iu3*R+`#npZ1$X1pCjH>pgzkBX|wf%Hay(GQsB~>+S zfW+6_iFq{L(0_XFv*wDR03**r zOuy>Co{tZGj`aoKHsOB)T7tsV*Lk3Elm|9N;M1&07*qoM6N<$ Ef@Y)M=l}o! delta 168 zcmV;Z09XI91dRfaZGQlYNklP?6h+|=m_U1Hz$tV96B6gJVF}|HCOUxy zJJ?cdYe?t`HWcI+gkEoXUha99f1)9HW|eTlrb7ktjB--YmG4*vr>Y^{^XS7k6m*}% zGAt5~A^Z^ZW)EdMNANA}96>SGj=0Sc}Tj<_VOcRa_|{c_$_ WVizA3Rq<^A00004nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1U#L=PLN^!uU@$m4`C0EXW}5@iOSYPsAFE+_!Pu~BT~g2HO*-al*{4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1T6d%8G= zSoFT_Q{+43AmWmK;~nFQW$6zXzCPH=wd~}qt}vmzMt7yGDkdGNoM7KsXA=|}moBZS z`1JJoU&enM{H>k~Wxo|Wz{H>udhhodjjqcX2P~(%1eUM}?3bC8d%g7YCY|%=>x9_e z&3}BsdsbAo`NDc`o3!tWnLm9K1SU!GdaSb3vFKsj^Ih`fw&&H(MY2`GUAxxJHt{V# sHbb(3<$&mcc~7*?e?EO-SNt=!bRLtK9d{-z13H_*)78&qol`;+09f;Jj{pDw diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed-de.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed-de.png deleted file mode 100644 index 201d4a298d5ecc34411ce89b499e360ee0786b17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 368 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1Uvdb&7< zSoB`KxX|x#g2>U2@wYj7(wY>_nDrY{`44cLG|2cV%+(}}4wt#8Kg-p;s{M@7^Tg4jNgveDKi8Tb)Z0{dtl~(V z>;p+7HsxDOe=jh)d}?P@rPHCh6B5?hlMZ~DaW|f`%14QhJI^4V`Nk@1N1rOE{SVgV zSwGk)wjrRoc+dKZUmTWqIXXOxcd>eM&SK?J<`awmWB!FVyVhK3LY_+|(BllAu6{1- HoD!M4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1S(dAc}; zSoB`Kr0d5VD8usM{yQZD36{_q^B7#6RGYYSm_)yN@C7h3^&dB%qE_nA#K=C4`GOXY zib7V`i+9c&e9cZzi<0>M?ChEPd+h(^0;ldsFn-8W{o_ci-}bW^nGT6{hpJq^&${3x z8P5FZ)1nzMrq0TbQ!<+wh1Xo=H0*wHTxR<0Bcc~e8YRP+=JvEJo^@sXzMtjJ1FqgV zmv1%9(Yu#*M&9C-MazM_T!-0#hx`__M>@to&w2m3XUT&waaqSxKN|cVY+B%y5M}ex z(&f&%ul9DAWSnP7_TN~MvsXQ5>7FZQ8s;Z6S%2+&pDJXiG5bH;K897tZS`DN`^o}+ O&*16m=d#Wzp$Pz)7?1A& diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed-ineff.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-mixed-ineff.png new file mode 100644 index 0000000000000000000000000000000000000000..45e794d438fd83a744fa3d3a35abc101fac68437 GIT binary patch literal 632 zcmV-;0*C#HP)Fl(8^>o>$3ztzDcjw1Hd%Rci{J>hy?7UI}MeG zH>aZsf4Nz3MMSJEkEEF^{K3e`g(suV`1;>$!LaAq=sI`8YN~P9iAZ=mfvURv!N^GS z{h(77&Q=$5soq0P$pHX9Mt*T;aEku1A2fFdO<-76E4Gu&Sc#tfZO>F%d)3=Hth?3C zz!#O)UUjtf>1;Id03b(I3p-v<_6$u#1z;|=h5~1fxiUsLebi)hVVThPRqT1Ts&`NC z-8BI3i_oHxE^HqNbE7|;bq|;8JpcqkOF4ko07_=8M24_^Ak4MCv5fvncXBV&Rve(z z08}JH2f(dY6B!Ui0g&VaZW_RRxw$VpAq7bv=hOT$04dMB0Sq-(7H{rK;QyU;4o(8l zSY5OMyfs8*<$P_a-=1gN1CfMyJ4XnNFXG70rKPs)p@^&sfU27Q50P+lchFSblY^pR8s_elA_U<56GoE5{)17E(!^bJp7J?)S4z zFUOa1rhdi&@Eo$!P*t?iQd{<@Q3!H-^ccBWj3pC P00000NkvXXu0mjfWHl$9 literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-overlay-inconsistent.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-overlay-inconsistent.png new file mode 100644 index 0000000000000000000000000000000000000000..6eafda829e5bbf053342a9dab20c491be746d33e GIT binary patch literal 305 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1UndAc}; zSoCh4c$)W+fq=_=-%|^j1h_JrycR5#xy0h3ux{rPk^H2O-0V4vJG#w-x}3q&)z4*}Q$iB})e~<8 literal 0 HcmV?d00001 diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-set.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoint-set.png deleted file mode 100644 index e12b41d30654c654651c93e19924ee49b9093c21..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 383 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1V4c)B=- zSoFS~XzRu7DA6|m{q2OcnxejnVtFF7BW`udIfUJq5~tWZU10qNUx7Q^84J}tCMs!X zWnR)aVdms9QF3kQY;y09A$Num~|KHb{NmX8p=HYJLCf-u~w+56pFX&f~UjfmnxR zbd$`R^XuHcYTvfbEMl^VOS|ayz{JeqouI?%pq#n37e${s~dN ZZ)aC)sD1snx5Jfj#EXw7LUG@hgb)+OqmIR7W_O;oha^FOqUkEi%7xP=w zdC_xo;GFY3@AI7ZJ%TMDr7QuC0@Z*7zQtm(p{lB?6TnNL9e)S_Yhn|zRMm8!e^l*w zHThlnJih{fyVOkH$bEUf>4muVFfV$?n{s&X(wo#dm!Czh4TPdWZ(rD!T>Z1b(Zu)| zawZ|>+~B{Fa)hTLsk04e5=P}fC_2^oIAn8JOaMd@`afskekQLz&xu-hhFkdns;2wA zePNr!Vq)l0Gk*ZBzF|5T1spY&dXQO{noChdnpZEPG*g zg8J5zgs-=qQUK?d_)4aFB%#yXGmNU~83(fqzi7Mn7Jsdx0Se9FKEjN)0#Ks>Qfz4E z|3M%C))XMJ)4pRv{@~>nT()$CNJ6LDvYj^<)3FS!b0P{b*j`t$wpIRCUog{+>@$G& z{pBfOND)FTb~V-n%!Qem%8(5Q*)0VqE-Jv&=z1cASTY959(W&}>hy=~TMd%o4j{Uq z)9Y!N?{hfhs1-tJ`2a$QxRkO5818=d!M?ouJLl0N37z7i0)yNlgwWEDnFT4O?Q2}` z@xN2gzMlM%_vvR60C$<0kHBfV(3y3q<@7plhDN?2nvEj zopf-qw40lQs~sX36hs9}HSlz3!AM1tshf-7*s&QJ#Gi%Ip}ylvCqXQ^s-DK86khdG=Jo1VH=aYJLEEs$^<5& z&t@cdzB7B1jcqjWoP%-XzsUHN5uP#|t%gZDZ9>M+D(=K^$g#`=EaJ^7OKGtR{}ssT zwQ#_dDLN0iGs7cDcwfRxv4-mj(o<~gbE$!qxQ+N4xRLM}XQG|GC3e1W95IxXnGWo%`HP9amE+5ZG(s8w+*1PiUSlGJ!rZoH0me_%L9;q@9p5 zca@J-4%PSWo53QL#LWX^Cd_M-wN^#O{b&PO3ku480369s8Ms` z_xSL9p7%!)MIw?6lmPnx0Y3SBzOMB2^m4!jlmSj)MMZ!H&wqUImZQgGZ5#G$A7_^U zFdGtyd3{Ifik_=0%`x5vTi9f~NoycTW$Zt2wzJp!%Jz6r7YHr?SJC_ax5(so_1O%L zt6|gN7zBcB1B%poW@oQ=r0RaJP6R6QjnrGr95E#WU^{=A3&l+vTFp7dH2|jOmRfh7 zeJZNviQITGCV!D~yWLV+S}KjT$jfG=u@=eAKj;H2nusJjdOX%ZXqk$9BbQDb*pSI$ zv9Q;u!)SmS`yPx2h)*%5iAcW&l(>fc8bH0p9BnB8+`abzF|}~p*-cfKjc`$&0!m_m zjH$WRX&+weIhr(0qq4G+Kxi4v#Z1mG0c2``D77iKL4Se(pilrS8Zfh8zXN~)@4}V= z24;Q$u*S@2KxbJ_+6sW(J+Wl~d&f8cWqGM#pi5Iq{XA2c=}e4|<=KZXG_;Rx3SVy< z#Wmz7DIt#P!mJjh)Z+Rf>79Mvkt#>8UIfbX(l}=^b2MWY0DaR7*gM8?4f#+vm^W^XD8tRm00kU(@1H58}Qxp9@Zjge{z^`GW;&G z{DYNku77}0wfX~Hz%$kZrL4cHIrLI$MMJDWU^C^mPB&vKrdVLIE&Qs%YNp0LiUXU^ zI6G$Jol=R@h&g+O204v{^PIVin=pg|^}-fX+8?Z-myjp@nI^A)#6YC46RY*KV S_+icf0000XR$;MJ&Xm%6RhX%ZaKh%awdeHl z@co|m`964s7E(%e0DfQ}pn%W0TrTc#I8FjHKmdpVHE9B^nSWwsGMdN)pQKl`5BV|x zZmS91i`(kr&||q8(PeLjtIqHQ_e&Md={LX!W6A9E@KnlDTKlu9(Zc*I)Z%wpc0=@9 z)tPz+r3$-&A!$sGC9`iscaj#RT0ANBko5SYExzY8*EN7lu`(H+O0|Rm#C8DBr9({5 z!IuI|&%v1+-hX8vp(&+0qKQnfx%MX6`!SeJ*sU-+Va09*L$|$0DK)PL{4;M>wDxpe zzmA3p#RuS+mr#5G@9|Rt`1OG6W1+kq4ucxTUlUML{^*c{sn;Aw6*P6 S{B{5U00{s|MNUMnLSTZXfdIV# delta 228 zcmVA6?4H2%sV(&s2GR-(IY&Uegya~) zYM-RAPMg*`X+^M?O_52nI3JLwnVI+F|IW*z|KSahexg_MRGK+&BNV2$&Q_^IK!2d-f diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-make-effective.png b/Ghidra/Debug/Debugger/src/main/resources/images/breakpoints-make-effective.png index b1e1ab4ede499f1cfbdafd425a95c7924f5cffe9..d220d56af110fe6bccf99ab417192f99b205bbd5 100644 GIT binary patch delta 302 zcmV+}0nz^b0+IueZGQm|Nkl{wevsSZl||m`eZ? z03ew==i($u3NOG~J66>f0Kp$qq^zncOw)AT1{eT9Gdb8bNq>%vFkwD7wV*@Id5)>;jX5s=$)nF8=iDJ4;W zh;)GtwjqE9aL!d)>jx2;UAUj}{k~np3*TdQKvhBy*D4nJ z@ErkR#;MwT(m+AU64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1SZdb&7< zSoAKvWaxc3K!o*y{#l(%=gz)~sCVUF*V?e(dBVTWNB8U%mRNbco^bQ(*44YFIeimo zS5r9ngtcnf_jeEHR==~J&Mj!#o<3=Y`kB{3eW9lmH3IDa9a(a8zE|4!nsUyb1@CMQ zN-s0yER#6CrkT%(E9R(*ciq1oznmr(-F>alu&O_YrAXuE9;3Cp^Rv3&JG<>T$bL-s zUx&5LE05)}kEOb2-3WjF+t1%>qFl!$BCr8*}Z!u_Al@;V-wP_mimage/svg+xml - + - - - - + + diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-set.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-disable-ineff.svg similarity index 55% rename from Ghidra/Debug/Debugger/src/main/svg/breakpoint-set.svg rename to Ghidra/Debug/Debugger/src/main/svg/breakpoint-disable-ineff.svg index bfb8ea0539..3778a7e78a 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-set.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-disable-ineff.svg @@ -20,21 +20,18 @@ image/svg+xml - + - - - - + + diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-disable.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-disable.svg index a3d85e2ff0..ac1fb6a8d9 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-disable.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-disable.svg @@ -5,11 +5,11 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - id="SVGRoot" - version="1.1" - viewBox="0 0 16 16" + width="16px" height="16px" - width="16px"> + viewBox="0 0 16 16" + version="1.1" + id="SVGRoot"> image/svg+xml - + - - - + + diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-ineffective-e.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-enable-ineff.svg similarity index 55% rename from Ghidra/Debug/Debugger/src/main/svg/breakpoint-ineffective-e.svg rename to Ghidra/Debug/Debugger/src/main/svg/breakpoint-enable-ineff.svg index e0732afc1e..9c37ecdce6 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-ineffective-e.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-enable-ineff.svg @@ -5,11 +5,11 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - width="16px" - height="16px" - viewBox="0 0 16 16" + id="SVGRoot" version="1.1" - id="SVGRoot"> + viewBox="0 0 16 16" + height="16px" + width="16px"> - - - + + diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-enable.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-enable.svg index 63f316fab7..9bcad1474a 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-enable.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-enable.svg @@ -5,11 +5,11 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - id="SVGRoot" - version="1.1" - viewBox="0 0 16 16" + width="16px" height="16px" - width="16px"> + viewBox="0 0 16 16" + version="1.1" + id="SVGRoot"> image/svg+xml - + - - - + + diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-ed.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-ed.svg deleted file mode 100644 index 49bd3efaee..0000000000 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-ed.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - image/svg+xml - - - - - - - - - - - - diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-ineffective-d.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-ineff.svg similarity index 56% rename from Ghidra/Debug/Debugger/src/main/svg/breakpoint-ineffective-d.svg rename to Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-ineff.svg index 980d822578..2187a30cbe 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-ineffective-d.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-ineff.svg @@ -24,11 +24,14 @@ - - - + + diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-de.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed.svg similarity index 53% rename from Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-de.svg rename to Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed.svg index 2defba113a..3c6da09bc6 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed-de.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-mixed.svg @@ -5,11 +5,11 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - width="16px" - height="16px" - viewBox="0 0 16 16" + id="SVGRoot" version="1.1" - id="SVGRoot"> + viewBox="0 0 16 16" + height="16px" + width="16px"> - - - + + diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoint-overlay-inconsistent.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-overlay-inconsistent.svg new file mode 100644 index 0000000000..2b69ed0cfa --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoint-overlay-inconsistent.svg @@ -0,0 +1,46 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoints-clear-all.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoints-clear-all.svg index 078e83d778..dc4c966d9a 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoints-clear-all.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoints-clear-all.svg @@ -20,39 +20,29 @@ image/svg+xml - + + + + id="g4631"> + id="rect4552" + d="m 11.865234,0.4 -1.730468,1.0007567 0.634765,1.0984867 H 9.5 v 2.0015133 h 1.269531 L 10.134766,5.5992434 11.865234,6.6 12.5,5.4995587 13.134766,6.6 14.865234,5.5992434 14.230469,4.5007567 H 15.5 V 2.4992434 H 14.230469 L 14.865234,1.4007567 13.134766,0.4 12.5,1.5004413 Z" + style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.50056732;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> - - - + id="rect818" + d="m 11.683594,1 -0.867188,0.5173808 0.816406,1.4652385 H 10 v 1.0347615 h 1.632812 L 10.816406,5.4826192 11.683594,6 12.5,4.5367825 13.316406,6 14.183594,5.4826192 13.367188,4.0173808 H 15 V 2.9826193 H 13.367188 L 14.183594,1.5173808 13.316406,1 12.5,2.4632175 Z" + style="fill:#d4aa00;fill-opacity:1;stroke:none;stroke-width:1.52584839;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoints-disable-all.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoints-disable-all.svg index 09fee1b484..92c66774a1 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoints-disable-all.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoints-disable-all.svg @@ -20,23 +20,29 @@ image/svg+xml - + + + + id="g4631"> + id="rect4552" + d="m 11.865234,0.4 -1.730468,1.0007567 0.634765,1.0984867 H 9.5 v 2.0015133 h 1.269531 L 10.134766,5.5992434 11.865234,6.6 12.5,5.4995587 13.134766,6.6 14.865234,5.5992434 14.230469,4.5007567 H 15.5 V 2.4992434 H 14.230469 L 14.865234,1.4007567 13.134766,0.4 12.5,1.5004413 Z" + style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.50056732;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> - + id="rect818" + d="m 11.683594,1 -0.867188,0.5173808 0.816406,1.4652385 H 10 v 1.0347615 h 1.632812 L 10.816406,5.4826192 11.683594,6 12.5,4.5367825 13.316406,6 14.183594,5.4826192 13.367188,4.0173808 H 15 V 2.9826193 H 13.367188 L 14.183594,1.5173808 13.316406,1 12.5,2.4632175 Z" + style="fill:#d4aa00;fill-opacity:1;stroke:none;stroke-width:1.52584839;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoints-enable-all.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoints-enable-all.svg index 3e033f91bb..8d68697416 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoints-enable-all.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoints-enable-all.svg @@ -24,17 +24,28 @@ + + - - + id="g4588"> + + + + diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoints-make-effective.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoints-make-effective.svg index cfb79a2e09..1aa938dc7b 100644 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoints-make-effective.svg +++ b/Ghidra/Debug/Debugger/src/main/svg/breakpoints-make-effective.svg @@ -5,35 +5,11 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="16px" - height="16px" - viewBox="0 0 16 16" - version="1.1" id="SVGRoot" - sodipodi:docname="breakpoints-make-effective.svg" - inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"> - + version="1.1" + viewBox="0 0 16 16" + height="16px" + width="16px"> + cx="11" + id="path821" + style="opacity:1;fill:#0166a9;fill-opacity:1;stroke:none;stroke-width:1.14285719;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + style="opacity:1;fill:#7f7f7f;fill-opacity:1;stroke:none;stroke-width:1.14285719;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> diff --git a/Ghidra/Debug/Debugger/src/main/svg/breakpoints.svg b/Ghidra/Debug/Debugger/src/main/svg/breakpoints.svg deleted file mode 100644 index 69ec53e769..0000000000 --- a/Ghidra/Debug/Debugger/src/main/svg/breakpoints.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - image/svg+xml - - - - - - - - - - diff --git a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginScreenShots.java b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginScreenShots.java index 9ce12258c8..790667fded 100644 --- a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginScreenShots.java +++ b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginScreenShots.java @@ -21,25 +21,41 @@ import java.util.Set; import org.junit.Before; import org.junit.Test; +import com.google.common.collect.Range; + import generic.Unique; import ghidra.app.plugin.core.codebrowser.CodeViewerProvider; +import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest.TestDebuggerTargetTraceMapper; import ghidra.app.plugin.core.debug.service.breakpoint.DebuggerLogicalBreakpointServicePlugin; +import ghidra.app.plugin.core.debug.service.model.DebuggerModelServiceProxyPlugin; +import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin; +import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin; import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin; import ghidra.app.services.*; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.app.util.viewer.listingpanel.ListingPanel; +import ghidra.dbg.model.TestDebuggerModelBuilder; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Program; import ghidra.program.util.ProgramLocation; +import ghidra.trace.model.DefaultTraceLocation; +import ghidra.trace.model.Trace; import ghidra.trace.model.breakpoint.TraceBreakpointKind; import ghidra.util.Msg; +import ghidra.util.database.UndoableTransaction; import ghidra.util.task.TaskMonitor; import help.screenshot.GhidraScreenShotGenerator; public class DebuggerBreakpointMarkerPluginScreenShots extends GhidraScreenShotGenerator { + private DebuggerModelService modelService; + private DebuggerTraceManagerService traceManager; + private DebuggerStaticMappingService mappingService; private DebuggerLogicalBreakpointService breakpointService; private DebuggerBreakpointMarkerPlugin breakpointMarkerPlugin; private ProgramManager programManager; + private TestDebuggerModelBuilder mb; + private CodeViewerProvider listing; protected static Address addr(Program program, long offset) { @@ -48,6 +64,9 @@ public class DebuggerBreakpointMarkerPluginScreenShots extends GhidraScreenShotG @Before public void setUpMine() throws Exception { + modelService = addPlugin(tool, DebuggerModelServiceProxyPlugin.class); + traceManager = addPlugin(tool, DebuggerTraceManagerServicePlugin.class); + mappingService = addPlugin(tool, DebuggerStaticMappingServicePlugin.class); breakpointService = addPlugin(tool, DebuggerLogicalBreakpointServicePlugin.class); breakpointMarkerPlugin = addPlugin(tool, DebuggerBreakpointMarkerPlugin.class); programManager = addPlugin(tool, ProgramManagerPlugin.class); @@ -55,17 +74,36 @@ public class DebuggerBreakpointMarkerPluginScreenShots extends GhidraScreenShotG listing = waitForComponentProvider(CodeViewerProvider.class); program = programManager.getCurrentProgram(); + + mb = new TestDebuggerModelBuilder(); } @Test public void testCaptureDebuggerBreakpointMarkerPlugin() throws Throwable { ListingPanel panel = listing.getListingPanel(); + mb.createTestModel(); + modelService.addModel(mb.testModel); + mb.createTestProcessesAndThreads(); + TestDebuggerTargetTraceMapper mapper = new TestDebuggerTargetTraceMapper(mb.testProcess1); + TraceRecorder recorder = + modelService.recordTarget(mb.testProcess1, mapper, ActionSource.AUTOMATIC); + Trace trace = recorder.getTrace(); + + traceManager.openTrace(trace); + traceManager.activateTrace(trace); + tool.getProject() .getProjectData() .getRootFolder() .createFile("WinHelloCPP", program, TaskMonitor.DUMMY); + try (UndoableTransaction tid = UndoableTransaction.start(trace, "Add Mapping", true)) { + mappingService.addIdentityMapping(trace, program, Range.atLeast(0L), true); + } + waitForValue(() -> mappingService.getOpenMappedLocation( + new DefaultTraceLocation(trace, null, Range.singleton(0L), mb.addr(0x00401c60)))); + Msg.debug(this, "Placing breakpoint"); breakpointService.placeBreakpointAt(program, addr(program, 0x00401c60), 1, Set.of(TraceBreakpointKind.SW_EXECUTE), ""); @@ -73,7 +111,9 @@ public class DebuggerBreakpointMarkerPluginScreenShots extends GhidraScreenShotG Msg.debug(this, "Disabling breakpoint"); LogicalBreakpoint lb = waitForValue(() -> Unique.assertAtMostOne( breakpointService.getBreakpointsAt(program, addr(program, 0x00401c60)))); + lb.disable(); + waitForCondition(() -> lb.computeState() == State.DISABLED); Msg.debug(this, "Placing another"); breakpointService.placeBreakpointAt(program, addr(program, 0x00401c63), 1, diff --git a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsPluginScreenShots.java b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsPluginScreenShots.java index eae0530f72..145dd242ef 100644 --- a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsPluginScreenShots.java +++ b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsPluginScreenShots.java @@ -162,15 +162,17 @@ public class DebuggerBreakpointsPluginScreenShots extends GhidraScreenShotGenera try (UndoableTransaction tid = UndoableTransaction.start(program, "Add breakpoint", true)) { program.getBookmarkManager() .setBookmark(addr(program, 0x00401234), - LogicalBreakpoint.BREAKPOINT_ENABLED_BOOKMARK_TYPE, "SW_EXECUTE;1", ""); + LogicalBreakpoint.BREAKPOINT_ENABLED_BOOKMARK_TYPE, "SW_EXECUTE;1", + "before connect"); program.getBookmarkManager() - .setBookmark(addr(program, 0x00402345), - LogicalBreakpoint.BREAKPOINT_DISABLED_BOOKMARK_TYPE, "SW_EXECUTE;1", ""); + .setBookmark(addr(program, 0x00604321), + LogicalBreakpoint.BREAKPOINT_ENABLED_BOOKMARK_TYPE, "WRITE;4", + "write version"); } waitForPass(() -> { Set allBreakpoints = breakpointService.getAllBreakpoints(); - assertEquals(3, allBreakpoints.size()); + assertEquals(2, allBreakpoints.size()); }); waitForPass(() -> { assertFalse(bpt.isEnabled(0)); @@ -180,7 +182,7 @@ public class DebuggerBreakpointsPluginScreenShots extends GhidraScreenShotGenera * there are 3 for just a moment, and then additional callbacks mess things up. */ waitForPass(() -> { - assertEquals(3, provider.breakpointTable.getRowCount()); + assertEquals(2, provider.breakpointTable.getRowCount()); assertEquals(3, provider.locationTable.getRowCount()); }); diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginTest.java index ccc24403b5..ae6bc62311 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginTest.java @@ -21,8 +21,11 @@ import static org.junit.Assert.assertTrue; import java.awt.*; import java.awt.event.MouseEvent; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.swing.MenuElement; import javax.swing.SwingUtilities; @@ -40,12 +43,11 @@ import generic.test.category.NightlyCategory; import ghidra.app.context.ProgramLocationActionContext; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest; -import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.app.plugin.core.debug.gui.DebuggerResources.*; import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin; import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils; import ghidra.app.services.*; -import ghidra.app.services.LogicalBreakpoint.Enablement; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.app.util.viewer.listingpanel.ListingPanel; import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; import ghidra.dbg.target.TargetBreakpointSpecContainer; @@ -72,10 +74,8 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu protected static final long TIMEOUT_MILLIS = SystemUtilities.isInTestingBatchMode() ? 5000 : Long.MAX_VALUE; - protected static final Color D_COLOR = new Color(255, 192, 192); - protected static final Color E_COLOR = new Color(255, 128, 128); - protected static final Color DE_COLOR = new Color(255, 192, 128); - protected static final Color ED_COLOR = new Color(255, 128, 192); + protected static final Map COLOR_FOR_STATE = + Stream.of(State.values()).collect(Collectors.toMap(s -> s, s -> new Color(s.ordinal()))); protected DebuggerBreakpointMarkerPlugin breakpointMarkerPlugin; protected DebuggerListingPlugin listingPlugin; @@ -130,7 +130,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); assertEquals(program, lb.getProgram()); assertEquals(Set.of(trace), lb.getParticipatingTraces()); - assertEquals(Enablement.ENABLED, lb.computeEnablement()); + assertEquals(State.ENABLED, lb.computeState()); }); } @@ -164,22 +164,14 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu */ protected void hackMarkerBackgroundColors(Program p) throws Exception { SwingUtilities.invokeAndWait(() -> { - MarkerSet dd = - markerService.getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_DISABLED, p); - dd.setMarkerColor(D_COLOR); - dd.setColoringBackground(true); - MarkerSet ee = - markerService.getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_ENABLED, p); - ee.setMarkerColor(E_COLOR); - ee.setColoringBackground(true); - MarkerSet de = - markerService.getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_MIXED_DE, p); - de.setMarkerColor(DE_COLOR); - de.setColoringBackground(true); - MarkerSet ed = - markerService.getMarkerSet(DebuggerResources.MARKER_NAME_BREAKPOINT_MIXED_ED, p); - ed.setMarkerColor(ED_COLOR); - ed.setColoringBackground(true); + for (State state : State.values()) { + if (state == State.NONE) { + continue; + } + MarkerSet ms = markerService.getMarkerSet(state.display, p); + ms.setMarkerColor(COLOR_FOR_STATE.get(state)); + ms.setColoringBackground(true); + } }); } @@ -208,56 +200,56 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu } @Test - public void testProgramBreakpointMarked() throws Exception { + public void testBreakpointMarked() throws Exception { TraceRecorder recorder = addMappedBreakpointOpenAndWait(); Trace trace = recorder.getTrace(); + traceManager.activateTrace(trace); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); - Address addr = addr(program, 0x00400123); - hackMarkerBackgroundColors(program); - - waitForPass(() -> assertEquals(E_COLOR, getBackgroundColor(program, addr))); - - lb.disableForProgram(); - waitForDomainObject(program); - - waitForPass(() -> assertEquals(DE_COLOR, getBackgroundColor(program, addr))); - - lb.disableForTrace(trace); - waitForDomainObject(trace); - - waitForPass(() -> assertEquals(D_COLOR, getBackgroundColor(program, addr))); - - lb.enableForProgram(); - waitForDomainObject(program); - - waitForPass(() -> assertEquals(ED_COLOR, getBackgroundColor(program, addr))); - } - - @Test - public void testTraceBreakpointMarked() throws Exception { - TraceRecorder recorder = addMappedBreakpointOpenAndWait(); - Trace trace = recorder.getTrace(); - LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); - Address addr = addr(trace, 0x55550123); + Address dAddr = addr(trace, 0x55550123); + Address sAddr = addr(program, 0x00400123); TraceProgramView view = trace.getProgramView(); hackMarkerBackgroundColors(view); + hackMarkerBackgroundColors(program); - assertEquals(E_COLOR, getBackgroundColor(view, addr)); + waitForPass(() -> assertEquals(State.ENABLED, lb.computeStateForTrace(trace))); + waitForPass(() -> assertEquals(COLOR_FOR_STATE.get(State.ENABLED), + getBackgroundColor(view, dAddr))); + waitForPass(() -> assertEquals(State.ENABLED, lb.computeStateForProgram(program))); + waitForPass(() -> assertEquals(COLOR_FOR_STATE.get(State.ENABLED), + getBackgroundColor(program, sAddr))); lb.disableForProgram(); waitForDomainObject(program); - waitForPass(() -> assertEquals(ED_COLOR, getBackgroundColor(view, addr))); + waitForPass(() -> assertEquals(State.INCONSISTENT_ENABLED, lb.computeStateForTrace(trace))); + waitForPass(() -> assertEquals(COLOR_FOR_STATE.get(State.INCONSISTENT_ENABLED), + getBackgroundColor(view, dAddr))); + waitForPass( + () -> assertEquals(State.INCONSISTENT_DISABLED, lb.computeStateForProgram(program))); + waitForPass(() -> assertEquals(COLOR_FOR_STATE.get(State.INCONSISTENT_DISABLED), + getBackgroundColor(program, sAddr))); lb.disableForTrace(trace); waitForDomainObject(trace); - waitForPass(() -> assertEquals(D_COLOR, getBackgroundColor(view, addr))); + waitForPass(() -> assertEquals(State.DISABLED, lb.computeStateForTrace(trace))); + waitForPass(() -> assertEquals(COLOR_FOR_STATE.get(State.DISABLED), + getBackgroundColor(view, dAddr))); + waitForPass(() -> assertEquals(State.DISABLED, lb.computeStateForProgram(program))); + waitForPass(() -> assertEquals(COLOR_FOR_STATE.get(State.DISABLED), + getBackgroundColor(program, sAddr))); lb.enableForProgram(); waitForDomainObject(program); - waitForPass(() -> assertEquals(DE_COLOR, getBackgroundColor(view, addr))); + waitForPass( + () -> assertEquals(State.INCONSISTENT_DISABLED, lb.computeStateForTrace(trace))); + waitForPass(() -> assertEquals(COLOR_FOR_STATE.get(State.INCONSISTENT_DISABLED), + getBackgroundColor(view, dAddr))); + waitForPass( + () -> assertEquals(State.INCONSISTENT_ENABLED, lb.computeStateForProgram(program))); + waitForPass(() -> assertEquals(COLOR_FOR_STATE.get(State.INCONSISTENT_ENABLED), + getBackgroundColor(program, sAddr))); } protected static final Set POPUP_ACTIONS = Set.of(AbstractSetBreakpointAction.NAME, @@ -366,14 +358,15 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu AbstractDisableBreakpointAction.NAME, AbstractClearBreakpointAction.NAME)); pressEscape(); - lb.disableForProgram(); // Should not change anything + lb.disableForProgram(); // Adds "enable", which will only affect bookmark waitForDomainObject(program); clickListing(listingPlugin.getListingPanel(), addr(trace, 0x55550123), MouseEvent.BUTTON3); assertMenu(POPUP_ACTIONS, Set.of(AbstractSetBreakpointAction.NAME, AbstractToggleBreakpointAction.NAME, - AbstractDisableBreakpointAction.NAME, AbstractClearBreakpointAction.NAME)); + AbstractEnableBreakpointAction.NAME, AbstractDisableBreakpointAction.NAME, + AbstractClearBreakpointAction.NAME)); pressEscape(); lb.disableForTrace(trace); @@ -386,17 +379,18 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu AbstractEnableBreakpointAction.NAME, AbstractClearBreakpointAction.NAME)); pressEscape(); - lb.enableForProgram(); // Again, no change + lb.enableForProgram(); // This time, adds "disable", which will only affect bookmark waitForDomainObject(program); clickListing(listingPlugin.getListingPanel(), addr(trace, 0x55550123), MouseEvent.BUTTON3); assertMenu(POPUP_ACTIONS, Set.of(AbstractSetBreakpointAction.NAME, AbstractToggleBreakpointAction.NAME, - AbstractEnableBreakpointAction.NAME, AbstractClearBreakpointAction.NAME)); + AbstractEnableBreakpointAction.NAME, AbstractDisableBreakpointAction.NAME, + AbstractClearBreakpointAction.NAME)); // TODO: Should mixed trace enablement be considered? - // TODO: Margin, too? (Is there one?) + // TODO: Margin, too? } protected ProgramLocationActionContext staticCtx(Address address) { @@ -433,7 +427,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu waitForPass(() -> { LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); - assertEquals(Enablement.ENABLED, lb.computeEnablement()); + assertEquals(State.ENABLED, lb.computeState()); // TODO: Different cases for different expected default kinds? assertEquals(Set.of(TraceBreakpointKind.SW_EXECUTE), lb.getKinds()); }); @@ -460,7 +454,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu waitForPass(() -> { LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); - assertEquals(Enablement.ENABLED, lb.computeEnablement()); + assertEquals(State.ENABLED, lb.computeState()); // TODO: Different cases for different expected default kinds? assertEquals(Set.of(TraceBreakpointKind.READ, TraceBreakpointKind.WRITE), lb.getKinds()); @@ -475,12 +469,12 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu performAction(breakpointMarkerPlugin.actionToggleBreakpoint, staticCtx(addr(program, 0x00400123)), false); - waitForPass(() -> assertEquals(Enablement.DISABLED, lb.computeEnablement())); + waitForPass(() -> assertEquals(State.DISABLED, lb.computeState())); performAction(breakpointMarkerPlugin.actionToggleBreakpoint, staticCtx(addr(program, 0x00400123)), false); - waitForPass(() -> assertEquals(Enablement.ENABLED, lb.computeEnablement())); + waitForPass(() -> assertEquals(State.ENABLED, lb.computeState())); } @Test @@ -489,30 +483,39 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu Trace trace = recorder.getTrace(); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); + // NB. addMappedBreakpointOpenAndWait already makes this assertion. Just a reminder: + waitForPass(() -> assertEquals(State.ENABLED, lb.computeStateForTrace(trace))); + performAction(breakpointMarkerPlugin.actionToggleBreakpoint, dynamicCtx(trace, addr(trace, 0x55550123)), true); + waitForPass(() -> assertEquals(State.DISABLED, lb.computeStateForTrace(trace))); + + performAction(breakpointMarkerPlugin.actionToggleBreakpoint, + dynamicCtx(trace, addr(trace, 0x55550123)), true); + + waitForPass(() -> assertEquals(State.ENABLED, lb.computeStateForTrace(trace))); + + // TODO: Add a second trace breakpoint and verify program toggling behavior + // For now, just force an inconsistent state and see what happens when we toggle + + lb.disableForProgram(); + waitForPass(() -> assertEquals(State.INCONSISTENT_ENABLED, lb.computeStateForTrace(trace))); + + performAction(breakpointMarkerPlugin.actionToggleBreakpoint, + dynamicCtx(trace, addr(trace, 0x55550123)), true); + + // NB. toggling from dynamic view, this toggles trace bpt, not logical/program bpt + waitForPass(() -> assertEquals(State.DISABLED, lb.computeStateForTrace(trace))); + + lb.enableForProgram(); waitForPass( - () -> assertEquals(Enablement.DISABLED_ENABLED, lb.computeEnablementForTrace(trace))); + () -> assertEquals(State.INCONSISTENT_DISABLED, lb.computeStateForTrace(trace))); performAction(breakpointMarkerPlugin.actionToggleBreakpoint, dynamicCtx(trace, addr(trace, 0x55550123)), true); - waitForPass(() -> assertEquals(Enablement.ENABLED, lb.computeEnablementForTrace(trace))); - - lb.disable(); - waitForPass(() -> assertEquals(Enablement.DISABLED, lb.computeEnablementForTrace(trace))); - - performAction(breakpointMarkerPlugin.actionToggleBreakpoint, - dynamicCtx(trace, addr(trace, 0x55550123)), true); - - waitForPass( - () -> assertEquals(Enablement.ENABLED_DISABLED, lb.computeEnablementForTrace(trace))); - - performAction(breakpointMarkerPlugin.actionToggleBreakpoint, - dynamicCtx(trace, addr(trace, 0x55550123)), true); - - waitForPass(() -> assertEquals(Enablement.DISABLED, lb.computeEnablementForTrace(trace))); + waitForPass(() -> assertEquals(State.ENABLED, lb.computeStateForTrace(trace))); } protected void testActionSetBreakpointProgram(DockingAction action, @@ -529,7 +532,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu LogicalBreakpoint lb = Unique.assertOne( breakpointService.getBreakpointsAt(program, addr(program, 0x00400321))); assertEquals(expectedKinds, lb.getKinds()); - assertEquals(Enablement.ENABLED, lb.computeEnablement()); + assertEquals(State.ENABLED, lb.computeState()); assertEquals("Test name", lb.getName()); }); } @@ -548,8 +551,12 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu LogicalBreakpoint lb = Unique .assertOne(breakpointService.getBreakpointsAt(trace, addr(trace, 0x55550321))); assertEquals(expectedKinds, lb.getKinds()); - assertEquals(Enablement.ENABLED_DISABLED, lb.computeEnablementForTrace(trace)); + assertEquals(State.ENABLED, lb.computeStateForTrace(trace)); }); + /** + * TODO: Test with a second trace? ATM, the program state is auto-synced with single trace, + * so might be difficult to assess more complex state changes. + */ } @Test @@ -617,12 +624,12 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu addMappedBreakpointOpenAndWait(); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); lb.disable(); - waitForPass(() -> assertEquals(Enablement.DISABLED, lb.computeEnablement())); + waitForPass(() -> assertEquals(State.DISABLED, lb.computeState())); performAction(breakpointMarkerPlugin.actionEnableBreakpoint, staticCtx(addr(program, 0x00400123)), true); - waitForPass(() -> assertEquals(Enablement.ENABLED, lb.computeEnablement())); + waitForPass(() -> assertEquals(State.ENABLED, lb.computeState())); } @Test @@ -631,13 +638,13 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu Trace trace = recorder.getTrace(); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); lb.disable(); - waitForPass(() -> assertEquals(Enablement.DISABLED, lb.computeEnablement())); + waitForPass(() -> assertEquals(State.DISABLED, lb.computeState())); performAction(breakpointMarkerPlugin.actionEnableBreakpoint, dynamicCtx(trace, addr(trace, 0x55550123)), true); - waitForPass( - () -> assertEquals(Enablement.ENABLED_DISABLED, lb.computeEnablementForTrace(trace))); + waitForPass(() -> assertEquals(State.ENABLED, lb.computeStateForTrace(trace))); + // TODO: Same test but with multiple traces } @Test @@ -648,7 +655,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu performAction(breakpointMarkerPlugin.actionDisableBreakpoint, staticCtx(addr(program, 0x00400123)), true); - waitForPass(() -> assertEquals(Enablement.DISABLED, lb.computeEnablement())); + waitForPass(() -> assertEquals(State.DISABLED, lb.computeState())); } @Test @@ -660,8 +667,8 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu performAction(breakpointMarkerPlugin.actionDisableBreakpoint, dynamicCtx(trace, addr(trace, 0x55550123)), true); - waitForPass( - () -> assertEquals(Enablement.DISABLED_ENABLED, lb.computeEnablementForTrace(trace))); + waitForPass(() -> assertEquals(State.DISABLED, lb.computeStateForTrace(trace))); + // TODO: Same test but with multiple traces } @Test @@ -683,8 +690,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu performAction(breakpointMarkerPlugin.actionClearBreakpoint, dynamicCtx(trace, addr(trace, 0x55550123)), true); - // NB. Because it was deleted from the *trace context* - waitForPass(() -> assertEquals(Enablement.INEFFECTIVE_ENABLED, - lb.computeEnablementForTrace(trace))); + waitForPass(() -> assertEquals(State.NONE, lb.computeStateForTrace(trace))); + // TODO: Same test but with multiple traces } } diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProviderTest.java index f1e79b3f75..9538e2b30e 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProviderTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProviderTest.java @@ -37,7 +37,7 @@ import ghidra.app.plugin.core.debug.gui.breakpoint.DebuggerBreakpointsProvider.L import ghidra.app.plugin.core.debug.gui.console.DebuggerConsolePlugin; import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils; import ghidra.app.services.*; -import ghidra.app.services.LogicalBreakpoint.Enablement; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.async.AsyncTestUtils; import ghidra.dbg.model.TestTargetProcess; import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; @@ -137,7 +137,7 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge assertEquals("55550123", row.getAddress().toString()); assertEquals(trace, row.getDomainObject()); assertEquals("SW_EXECUTE", row.getKinds()); - assertEquals(Enablement.ENABLED, row.getEnablement()); + assertEquals(State.INCONSISTENT_ENABLED, row.getState()); } @Test @@ -156,16 +156,16 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge LogicalBreakpointRow row = Unique.assertOne(breakpointsProvider.breakpointTableModel.getModelData()); - assertEquals(Enablement.ENABLED, row.getEnablement()); + assertEquals(State.INCONSISTENT_ENABLED, row.getState()); // NB, the row does not take the value immediately, but via async callbacks row.setEnabled(false); - waitForPass(() -> assertEquals(Enablement.DISABLED, row.getEnablement())); + waitForPass(() -> assertEquals(State.INCONSISTENT_DISABLED, row.getState())); row.setEnabled(true); - waitForPass(() -> assertEquals(Enablement.ENABLED, row.getEnablement())); + waitForPass(() -> assertEquals(State.INCONSISTENT_ENABLED, row.getState())); } @Test @@ -180,7 +180,7 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge assertEquals("00400123", row.getAddress().toString()); assertEquals(program, row.getDomainObject()); assertEquals("SW_EXECUTE", row.getKinds()); - assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_ENABLED, row.getState()); } @Test @@ -192,17 +192,17 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge LogicalBreakpointRow row = Unique.assertOne(breakpointsProvider.breakpointTableModel.getModelData()); - assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_ENABLED, row.getState()); row.setEnabled(false); // Synchronous, but on swing thread waitForDomainObject(program); - assertEquals(Enablement.INEFFECTIVE_DISABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_DISABLED, row.getState()); row.setEnabled(true); waitForDomainObject(program); - assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_ENABLED, row.getState()); } @Test @@ -228,7 +228,7 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge LogicalBreakpoint lb = row.getLogicalBreakpoint(); assertEquals(program, lb.getProgram()); assertEquals(Set.of(trace), lb.getParticipatingTraces()); - assertEquals(Enablement.ENABLED, row.getEnablement()); + assertEquals(State.ENABLED, row.getState()); }); LogicalBreakpointRow row = @@ -238,22 +238,24 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge lb.disableForProgram(); waitForDomainObject(program); - assertEquals(Enablement.DISABLED_ENABLED, row.getEnablement()); + waitForPass(() -> assertEquals(State.INCONSISTENT_DISABLED, row.getState())); // NOTE: This acts on the corresponding target, not directly on trace lb.disableForTrace(trace); + waitForDomainObject(trace); - waitForPass(() -> assertEquals(Enablement.DISABLED, row.getEnablement())); + waitForPass(() -> assertEquals(State.DISABLED, row.getState())); lb.enableForProgram(); waitForDomainObject(program); - assertEquals(Enablement.ENABLED_DISABLED, row.getEnablement()); + waitForPass(() -> assertEquals(State.INCONSISTENT_ENABLED, row.getState())); // This duplicates the initial case, but without it, I just feel incomplete lb.enableForTrace(trace); + waitForDomainObject(trace); - waitForPass(() -> assertEquals(Enablement.ENABLED, row.getEnablement())); + waitForPass(() -> assertEquals(State.ENABLED, row.getState())); } @Test @@ -302,12 +304,12 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge breakpointsProvider.breakpointFilterPanel.setSelectedItem(row); waitForSwing(); - assertEquals(Enablement.INEFFECTIVE_DISABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_DISABLED, row.getState()); assertTrue(breakpointsProvider.actionEnableSelectedBreakpoints.isEnabled()); performAction(breakpointsProvider.actionEnableSelectedBreakpoints); - assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_ENABLED, row.getState()); assertTrue(breakpointsProvider.actionEnableSelectedBreakpoints.isEnabled()); breakpointsProvider.breakpointTable.clearSelection(); @@ -345,12 +347,12 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge row.setEnabled(false); waitForSwing(); - assertEquals(Enablement.INEFFECTIVE_DISABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_DISABLED, row.getState()); assertTrue(breakpointsProvider.actionEnableAllBreakpoints.isEnabled()); performAction(breakpointsProvider.actionEnableAllBreakpoints); - assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_ENABLED, row.getState()); assertTrue(breakpointsProvider.actionEnableAllBreakpoints.isEnabled()); // Bookmark part should actually be synchronous. @@ -378,12 +380,12 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge breakpointsProvider.breakpointFilterPanel.setSelectedItem(row); waitForSwing(); - assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement()); + waitForPass(() -> assertEquals(State.INEFFECTIVE_ENABLED, row.getState())); assertTrue(breakpointsProvider.actionDisableSelectedBreakpoints.isEnabled()); performAction(breakpointsProvider.actionDisableSelectedBreakpoints); - assertEquals(Enablement.INEFFECTIVE_DISABLED, row.getEnablement()); + waitForPass(() -> assertEquals(State.INEFFECTIVE_DISABLED, row.getState())); assertTrue(breakpointsProvider.actionDisableSelectedBreakpoints.isEnabled()); breakpointsProvider.breakpointTable.clearSelection(); @@ -417,12 +419,12 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge assertTrue(breakpointsProvider.actionDisableAllBreakpoints.isEnabled()); LogicalBreakpointRow row = Unique.assertOne(breakpointsProvider.breakpointTableModel.getModelData()); - assertEquals(Enablement.INEFFECTIVE_ENABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_ENABLED, row.getState()); assertTrue(breakpointsProvider.actionDisableAllBreakpoints.isEnabled()); performAction(breakpointsProvider.actionDisableAllBreakpoints); - assertEquals(Enablement.INEFFECTIVE_DISABLED, row.getEnablement()); + assertEquals(State.INEFFECTIVE_DISABLED, row.getState()); assertTrue(breakpointsProvider.actionDisableAllBreakpoints.isEnabled()); // Bookmark part should actually be synchronous. diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServiceTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServiceTest.java index c1beba7a53..a28c446881 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServiceTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServiceTest.java @@ -28,7 +28,7 @@ import generic.Unique; import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest; import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils; import ghidra.app.services.*; -import ghidra.app.services.LogicalBreakpoint.Enablement; +import ghidra.app.services.LogicalBreakpoint.State; import ghidra.async.AsyncReference; import ghidra.dbg.model.TestTargetMemoryRegion; import ghidra.dbg.model.TestTargetProcess; @@ -292,18 +292,22 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe } } - protected void addTargetAccessBreakpoint(TraceRecorder r) throws Throwable { + protected void addTargetAccessBreakpoint(TraceRecorder r, TestTargetMemoryRegion region) + throws Throwable { + TraceMemoryRegion traceRegion = + waitFor(() -> r.getTraceMemoryRegion(region), "Recorder missed region: " + region); + long offset = traceRegion.getMinAddress().getOffset() + 0x0123; TargetBreakpointSpecContainer cont = getBreakpointContainer(r); - cont.placeBreakpoint(mb.testModel.getAddress("ram", 0x56550123), + cont.placeBreakpoint(mb.addr(offset), Set.of(TargetBreakpointKind.READ, TargetBreakpointKind.WRITE)) .get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); } protected void addTargetSoftwareBreakpoint(TraceRecorder r, TestTargetMemoryRegion region) throws Throwable { - TraceMemoryRegion textRegion = + TraceMemoryRegion traceRegion = waitFor(() -> r.getTraceMemoryRegion(region), "Recorder missed region: " + region); - long offset = textRegion.getMinAddress().getOffset() + 0x0123; + long offset = traceRegion.getMinAddress().getOffset() + 0x0123; TargetBreakpointSpecContainer cont = getBreakpointContainer(r); cont.placeBreakpoint(mb.addr(offset), Set.of(TargetBreakpointKind.SW_EXECUTE)) .get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); @@ -367,11 +371,11 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(addr(trace, 0x56550123), enLb.getTraceAddress(trace)); assertEquals(Set.of(bpt), enLb.getTraceBreakpoints(trace)); assertEquals(Set.of(bpt), enLb.getTraceBreakpoints()); - assertEquals(Enablement.ENABLED, enLb.computeEnablementForTrace(trace)); + assertEquals(State.INCONSISTENT_ENABLED, enLb.computeStateForTrace(trace)); } - protected void assertLogicalBreakpointForLoneSoftwareBreakpoint(Trace trace) { - assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 0x55550123, 1); + protected void assertLogicalBreakpointForLoneSoftwareBreakpoint(Trace trace, int total) { + assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 0x55550123, total); } protected void assertLogicalBreakpointForLoneSoftwareBreakpoint(Trace trace, long offset, @@ -388,7 +392,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(addr(trace, offset), enLb.getTraceAddress(trace)); assertEquals(Set.of(bpt), enLb.getTraceBreakpoints(trace)); assertEquals(Set.of(bpt), enLb.getTraceBreakpoints()); - assertEquals(Enablement.ENABLED, enLb.computeEnablementForTrace(trace)); + assertEquals(State.INCONSISTENT_ENABLED, enLb.computeStateForTrace(trace)); } protected void assertLogicalBreakpointForMappedSoftwareBreakpoint(Trace trace) { @@ -396,8 +400,8 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe LogicalBreakpoint enLb = Unique .assertOne(breakpointService.getBreakpointsAt(program, addr(program, 0x00400123))); - assertNull(enLb.getProgramBookmark()); - assertEquals(Enablement.DISABLED_ENABLED, enLb.computeEnablementForProgram(program)); + assertNotNull(enLb.getProgramBookmark()); // Created automatically when trace breakpoint set. + assertEquals(State.ENABLED, enLb.computeStateForProgram(program)); assertEquals(Set.of(TraceBreakpointKind.SW_EXECUTE), enLb.getKinds()); TraceBreakpoint bpt = Unique.assertOne(trace.getBreakpointManager().getAllBreakpoints()); @@ -405,7 +409,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(addr(trace, 0x55550123), enLb.getTraceAddress(trace)); assertEquals(Set.of(bpt), enLb.getTraceBreakpoints(trace)); assertEquals(Set.of(bpt), enLb.getTraceBreakpoints()); - assertEquals(Enablement.ENABLED_DISABLED, enLb.computeEnablementForTrace(trace)); + assertEquals(State.ENABLED, enLb.computeStateForTrace(trace)); } protected void assertLogicalBreakpointsForUnmappedBookmarks() { @@ -417,7 +421,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(addr(program, 0x00400123), enLb.getProgramLocation().getAddress()); assertEquals(enBm, enLb.getProgramBookmark()); assertTrue(enLb.getMappedTraces().isEmpty()); - assertEquals(Enablement.INEFFECTIVE_ENABLED, enLb.computeEnablementForProgram(program)); + assertEquals(State.INEFFECTIVE_ENABLED, enLb.computeStateForProgram(program)); assertEquals(Set.of(TraceBreakpointKind.SW_EXECUTE), enLb.getKinds()); LogicalBreakpoint disLb = Unique @@ -426,7 +430,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(addr(program, 0x00400321), disLb.getProgramLocation().getAddress()); assertEquals(disBm, disLb.getProgramBookmark()); assertTrue(disLb.getMappedTraces().isEmpty()); - assertEquals(Enablement.INEFFECTIVE_DISABLED, disLb.computeEnablementForProgram(program)); + assertEquals(State.INEFFECTIVE_DISABLED, disLb.computeStateForProgram(program)); assertEquals(Set.of(TraceBreakpointKind.SW_EXECUTE), disLb.getKinds()); } @@ -442,8 +446,8 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(addr(trace, 0x55550123), enLb.getTraceAddress(trace)); assertEquals(Set.of(), enLb.getTraceBreakpoints(trace)); assertEquals(Set.of(), enLb.getTraceBreakpoints()); - assertEquals(Enablement.INEFFECTIVE_ENABLED, enLb.computeEnablementForProgram(program)); - assertEquals(Enablement.INEFFECTIVE_ENABLED, enLb.computeEnablementForTrace(trace)); + assertEquals(State.INEFFECTIVE_ENABLED, enLb.computeStateForProgram(program)); + assertEquals(State.NONE, enLb.computeStateForTrace(trace)); LogicalBreakpoint disLb = Unique .assertOne(breakpointService.getBreakpointsAt(program, addr(program, 0x00400321))); @@ -454,8 +458,8 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(addr(trace, 0x55550321), disLb.getTraceAddress(trace)); assertEquals(Set.of(), disLb.getTraceBreakpoints(trace)); assertEquals(Set.of(), disLb.getTraceBreakpoints()); - assertEquals(Enablement.INEFFECTIVE_DISABLED, disLb.computeEnablementForProgram(program)); - assertEquals(Enablement.INEFFECTIVE_DISABLED, disLb.computeEnablementForTrace(trace)); + assertEquals(State.INEFFECTIVE_DISABLED, disLb.computeStateForProgram(program)); + assertEquals(State.NONE, disLb.computeStateForTrace(trace)); } protected void assertLogicalBreakpointForMappedBookmarkAnd2TraceBreakpoints(Trace trace1, @@ -479,9 +483,9 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertNotEquals(Set.of(bpt1), enLb.getTraceBreakpoints(trace2)); // Sanity check assertEquals(Set.of(bpt1, bpt2), enLb.getTraceBreakpoints()); - assertEquals(Enablement.ENABLED, enLb.computeEnablementForProgram(program)); - assertEquals(Enablement.ENABLED, enLb.computeEnablementForTrace(trace1)); - assertEquals(Enablement.ENABLED, enLb.computeEnablementForTrace(trace2)); + assertEquals(State.ENABLED, enLb.computeStateForProgram(program)); + assertEquals(State.ENABLED, enLb.computeStateForTrace(trace1)); + assertEquals(State.ENABLED, enLb.computeStateForTrace(trace2)); LogicalBreakpoint disLb = Unique .assertOne(breakpointService.getBreakpointsAt(program, addr(program, 0x00400321))); @@ -494,9 +498,9 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(Set.of(), disLb.getTraceBreakpoints(trace1)); assertEquals(Set.of(), disLb.getTraceBreakpoints(trace2)); assertEquals(Set.of(), disLb.getTraceBreakpoints()); - assertEquals(Enablement.INEFFECTIVE_DISABLED, disLb.computeEnablementForProgram(program)); - assertEquals(Enablement.INEFFECTIVE_DISABLED, disLb.computeEnablementForTrace(trace1)); - assertEquals(Enablement.INEFFECTIVE_DISABLED, disLb.computeEnablementForTrace(trace2)); + assertEquals(State.INEFFECTIVE_DISABLED, disLb.computeStateForProgram(program)); + assertEquals(State.NONE, disLb.computeStateForTrace(trace1)); + assertEquals(State.NONE, disLb.computeStateForTrace(trace2)); } protected void assertLogicalBreakpointForMappedBookmarkAnd1TraceBreakpoint(Trace trace) { @@ -514,8 +518,8 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(Set.of(bpt), enLb.getTraceBreakpoints(trace)); assertEquals(Set.of(bpt), enLb.getTraceBreakpoints()); - assertEquals(Enablement.ENABLED, enLb.computeEnablementForProgram(program)); - assertEquals(Enablement.ENABLED, enLb.computeEnablementForTrace(trace)); + assertEquals(State.ENABLED, enLb.computeStateForProgram(program)); + assertEquals(State.ENABLED, enLb.computeStateForTrace(trace)); LogicalBreakpoint disLb = Unique .assertOne(breakpointService.getBreakpointsAt(program, addr(program, 0x00400321))); @@ -526,8 +530,8 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(addr(trace, 0x55550321), disLb.getTraceAddress(trace)); assertEquals(Set.of(), disLb.getTraceBreakpoints(trace)); assertEquals(Set.of(), disLb.getTraceBreakpoints()); - assertEquals(Enablement.INEFFECTIVE_DISABLED, disLb.computeEnablementForProgram(program)); - assertEquals(Enablement.INEFFECTIVE_DISABLED, disLb.computeEnablementForTrace(trace)); + assertEquals(State.INEFFECTIVE_DISABLED, disLb.computeStateForProgram(program)); + assertEquals(State.NONE, disLb.computeStateForTrace(trace)); } @Test @@ -551,8 +555,8 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe Trace trace = recorder1.getTrace(); traceManager.openTrace(trace); - addTargetDataRegion(mb.testProcess1); - addTargetAccessBreakpoint(recorder1); + TestTargetMemoryRegion data = addTargetDataRegion(mb.testProcess1); + addTargetAccessBreakpoint(recorder1, data); waitForPass(() -> { assertLogicalBreakpointForLoneAccessBreakpoint(trace); @@ -564,8 +568,8 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe startRecorder1(); Trace trace = recorder1.getTrace(); - addTargetDataRegion(mb.testProcess1); - addTargetAccessBreakpoint(recorder1); + TestTargetMemoryRegion data = addTargetDataRegion(mb.testProcess1); + addTargetAccessBreakpoint(recorder1, data); waitForDomainObject(trace); traceManager.openTrace(trace); @@ -698,7 +702,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe addTargetSoftwareBreakpoint(recorder1, text); waitForPass(() -> { - assertLogicalBreakpointForLoneSoftwareBreakpoint(trace); + assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1); }); changeListener.assertAccurate(); @@ -784,14 +788,12 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe addTextMapping(recorder1, text, program); addTargetSoftwareBreakpoint(recorder1, text); - waitForPass(() -> { - assertLogicalBreakpointForLoneSoftwareBreakpoint(trace); - }); + waitForPass(() -> assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1)); expectMappingChange(() -> programManager.openProgram(program)); waitForSwing(); - assertLogicalBreakpointForMappedSoftwareBreakpoint(trace); + waitForPass(() -> assertLogicalBreakpointForMappedSoftwareBreakpoint(trace)); } @Test @@ -908,7 +910,9 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe removeTargetSoftwareBreakpoint(recorder1); waitForPass(() -> { - assertTrue(breakpointService.getAllBreakpoints().isEmpty()); + // NB. The bookmark remains + LogicalBreakpoint one = Unique.assertOne(breakpointService.getAllBreakpoints()); + assertTrue(one.getTraceBreakpoints().isEmpty()); }); } @@ -936,7 +940,8 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe expectMappingChange(() -> removeTextMapping(recorder1, program)); waitForSwing(); - assertLogicalBreakpointForLoneSoftwareBreakpoint(trace); + // NB. Bookmark remains + assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 2); } @Test @@ -1175,7 +1180,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe addTargetSoftwareBreakpoint(recorder1, text); waitForPass(() -> { - assertLogicalBreakpointForLoneSoftwareBreakpoint(trace); + assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1); }); /** * NB. The recorder could still be mid transaction. If we open this transaction too soon, @@ -1196,7 +1201,8 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe } waitForPass(() -> { - assertLogicalBreakpointForLoneSoftwareBreakpoint(trace); + // NB. The bookmark is left over, so total increases + assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 2); }); } @@ -1226,7 +1232,10 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe } waitForDomainObject(trace); // Duplicative, but for form's sake.... - assertTrue(breakpointService.getAllBreakpoints().isEmpty()); + // Left over, because it was bookmarked automatically in program + // Still, there should be no trace breakpoint in it + LogicalBreakpoint one = Unique.assertOne(breakpointService.getAllBreakpoints()); + assertTrue(one.getTraceBreakpoints().isEmpty()); } @Test @@ -1280,7 +1289,9 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe expectMappingChange(() -> undo(trace)); - assertTrue(breakpointService.getAllBreakpoints().isEmpty()); + // NB. The bookmark remains + LogicalBreakpoint one = Unique.assertOne(breakpointService.getAllBreakpoints()); + assertTrue(one.getTraceBreakpoints().isEmpty()); expectMappingChange(() -> redo(trace)); @@ -1331,14 +1342,14 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe addTargetSoftwareBreakpoint(recorder1, text); waitForPass(() -> { - assertLogicalBreakpointForLoneSoftwareBreakpoint(trace); + assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1); }); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); waitOn(lb.disable()); waitForPass(() -> { - assertEquals(Enablement.DISABLED, lb.computeEnablement()); + assertEquals(State.INCONSISTENT_DISABLED, lb.computeState()); }); // Simulate a step, which should also cause snap advance in recorder @@ -1347,12 +1358,12 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe waitOn(mb.testModel.flushEvents()); waitForPass(() -> { assertEquals(oldSnap + 1, recorder1.getSnap()); - assertEquals(Enablement.DISABLED, lb.computeEnablement()); + assertEquals(State.INCONSISTENT_DISABLED, lb.computeState()); }); waitOn(lb.enable()); waitForPass(() -> { - assertEquals(Enablement.ENABLED, lb.computeEnablement()); + assertEquals(State.INCONSISTENT_ENABLED, lb.computeState()); }); } @@ -1367,7 +1378,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe addTargetSoftwareBreakpoint(recorder1, text); waitForPass(() -> { - assertLogicalBreakpointForLoneSoftwareBreakpoint(trace); + assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1); }); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); @@ -1390,7 +1401,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe addTargetSoftwareBreakpoint(recorder1, text); waitForDomainObject(trace); - waitForPass(() -> assertLogicalBreakpointForLoneSoftwareBreakpoint(trace)); + waitForPass(() -> assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1)); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); @@ -1419,7 +1430,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe addTargetSoftwareBreakpoint(recorder1, text); waitForPass(() -> { - assertLogicalBreakpointForLoneSoftwareBreakpoint(trace); + assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1); }); // NOTE: Still recording in the background @@ -1428,4 +1439,105 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe assertEquals(0, breakpointService.getAllBreakpoints().size()); } + + @Test + public void testRecordThenAddTwoBreakpointsDisable1Mixed() throws Throwable { + startRecorder1(); + Trace trace = recorder1.getTrace(); + traceManager.openTrace(trace); + + createProgramFromTrace(trace); + intoProject(program); + programManager.openProgram(program); + + addProgramTextBlock(program); + TestTargetMemoryRegion text = addTargetTextRegion(mb.testProcess1); + + addTextMapping(recorder1, text, program); + waitForSwing(); + + addProgramBreakpoints(program); + addTargetSoftwareBreakpoint(recorder1, text); + addTargetSoftwareBreakpoint(recorder1, text); + + waitForPass(() -> { + assertEquals(2, breakpointService.getAllBreakpoints().size()); + + LogicalBreakpoint lb = Unique.assertOne( + breakpointService.getBreakpointsAt(program, addr(program, 0x00400123))); + assertEquals(program, lb.getProgram()); + assertEquals(Set.of(trace), lb.getMappedTraces()); + + assertEquals(2, lb.getTraceBreakpoints().size()); + }); + + LogicalBreakpoint lb = Unique + .assertOne(breakpointService.getBreakpointsAt(program, addr(program, 0x00400123))); + Set locs = lb.getTraceBreakpoints(); + + TraceBreakpoint bpt0 = + locs.stream().filter(b -> b.getName().equals("0")).findAny().orElseThrow(); + TraceBreakpoint bpt1 = + locs.stream().filter(b -> b.getName().equals("1")).findAny().orElseThrow(); + breakpointService.disableLocs(Set.of(bpt0)); + + waitForPass(() -> { + assertEquals(State.INCONSISTENT_ENABLED, lb.computeState()); + assertEquals(State.INCONSISTENT_MIXED, lb.computeStateForTrace(trace)); + assertEquals(State.INCONSISTENT_DISABLED, lb.computeStateForLocation(bpt0)); + assertEquals(State.ENABLED, lb.computeStateForLocation(bpt1)); + }); + } + + @Test + public void testRecordThenAddTwoKindsOfBreakpointsDisable1Mixed() throws Throwable { + startRecorder1(); + Trace trace = recorder1.getTrace(); + traceManager.openTrace(trace); + + createProgramFromTrace(trace); + intoProject(program); + programManager.openProgram(program); + + addProgramTextBlock(program); + TestTargetMemoryRegion text = addTargetTextRegion(mb.testProcess1); + + addTextMapping(recorder1, text, program); + waitForSwing(); + + addProgramBreakpoints(program); + addTargetSoftwareBreakpoint(recorder1, text); + addTargetAccessBreakpoint(recorder1, text); + + waitForPass(() -> { + assertEquals(3, breakpointService.getAllBreakpoints().size()); + + Set lbs = + breakpointService.getBreakpointsAt(program, addr(program, 0x00400123)); + assertEquals(2, lbs.size()); + lbs.stream() + .filter(l -> l.getKinds().contains(TraceBreakpointKind.SW_EXECUTE)) + .findAny() + .orElseThrow(); + lbs.stream() + .filter(l -> l.getKinds().contains(TraceBreakpointKind.READ)) + .findAny() + .orElseThrow(); + }); + Set lbs = + breakpointService.getBreakpointsAt(program, addr(program, 0x00400123)); + LogicalBreakpoint lbEx = lbs.stream() + .filter(l -> l.getKinds().contains(TraceBreakpointKind.SW_EXECUTE)) + .findAny() + .orElseThrow(); + LogicalBreakpoint lbRw = lbs.stream() + .filter(l -> l.getKinds().contains(TraceBreakpointKind.READ)) + .findAny() + .orElseThrow(); + waitOn(lbEx.disable()); + + // TODO: This is more a test for the marker plugin, no? + waitForPass( + () -> assertEquals(State.MIXED, lbEx.computeState().sameAdddress(lbRw.computeState()))); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/MarkerManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/MarkerManager.java index d43dda9ffb..ea3aad4482 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/MarkerManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/MarkerManager.java @@ -20,7 +20,7 @@ import java.awt.Graphics; import java.awt.event.MouseEvent; import java.util.*; -import javax.swing.ImageIcon; +import javax.swing.Icon; import javax.swing.JToolTip; import javax.swing.event.ChangeListener; @@ -135,7 +135,7 @@ public class MarkerManager implements MarkerService { @Override public MarkerSet createPointMarker(String name, String markerDescription, Program program, int priority, boolean showMarkers, boolean showNavigation, boolean colorBackground, - Color color, ImageIcon icon) { + Color color, Icon icon) { MarkerSetImpl markers = new PointMarkerSet(this, name, markerDescription, priority, showMarkers, showNavigation, colorBackground, color, icon, program); @@ -146,7 +146,7 @@ public class MarkerManager implements MarkerService { @Override public MarkerSet createPointMarker(String name, String markerDescription, Program program, int priority, boolean showMarkers, boolean showNavigation, boolean colorBackground, - Color color, ImageIcon icon, boolean isPreferred) { + Color color, Icon icon, boolean isPreferred) { MarkerSetImpl markers = new PointMarkerSet(this, name, markerDescription, priority, showMarkers, showNavigation, colorBackground, color, icon, isPreferred, program); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/PointMarkerSet.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/PointMarkerSet.java index c8cbc2c4bb..5e241effab 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/PointMarkerSet.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/marker/PointMarkerSet.java @@ -21,6 +21,7 @@ import java.awt.image.ImageObserver; import java.util.Iterator; import java.util.List; +import javax.swing.Icon; import javax.swing.ImageIcon; import ghidra.app.util.viewer.listingpanel.VerticalPixelAddressMap; @@ -55,16 +56,16 @@ class PointMarkerSet extends MarkerSetImpl { */ PointMarkerSet(MarkerManager navigationManager, String name, String desc, int priority, boolean showMarkers, boolean showNavigation, boolean colorBackground, Color markerColor, - ImageIcon icon, boolean isPreferred, Program program) { + Icon icon, boolean isPreferred, Program program) { super(navigationManager, program, name, desc, priority, showMarkers, showNavigation, colorBackground, markerColor, isPreferred); if (icon == null) { icon = ResourceManager.loadImage("images/warning.png"); } - icon = ResourceManager.getScaledIcon(icon, 16, 16, Image.SCALE_SMOOTH); - image = icon.getImage(); - imageObserver = icon.getImageObserver(); + ImageIcon imageIcon = ResourceManager.getScaledIcon(icon, 16, 16, Image.SCALE_SMOOTH); + image = imageIcon.getImage(); + imageObserver = imageIcon.getImageObserver(); if (markerColor != null) { fillColor = getFillColor(markerColor); } @@ -86,7 +87,7 @@ class PointMarkerSet extends MarkerSetImpl { */ PointMarkerSet(MarkerManager navigationManager, String name, String desc, int priority, boolean showMarkers, boolean showNavigation, boolean colorBackground, Color markerColor, - ImageIcon icon, Program program) { + Icon icon, Program program) { this(navigationManager, name, desc, priority, showMarkers, showNavigation, colorBackground, markerColor, icon, true, program); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/services/MarkerService.java b/Ghidra/Features/Base/src/main/java/ghidra/app/services/MarkerService.java index 3a4bc250e8..8d8e2e0528 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/services/MarkerService.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/services/MarkerService.java @@ -17,7 +17,7 @@ package ghidra.app.services; import java.awt.Color; -import javax.swing.ImageIcon; +import javax.swing.Icon; import javax.swing.event.ChangeListener; import ghidra.app.plugin.core.marker.*; @@ -166,7 +166,7 @@ public interface MarkerService { */ public MarkerSet createPointMarker(String name, String markerDescription, Program program, int priority, boolean showMarkers, boolean showNavigation, boolean colorBackground, - Color color, ImageIcon icon); + Color color, Icon icon); /** * Create a Marker display which shows point type markers. @@ -186,7 +186,7 @@ public interface MarkerService { */ public MarkerSet createPointMarker(String name, String markerDescription, Program program, int priority, boolean showMarkers, boolean showNavigation, boolean colorBackground, - Color color, ImageIcon icon, boolean isPreferred); + Color color, Icon icon, boolean isPreferred); /** * Remove the marker set

Breakpoints refer to any mechanism which may trap execution based on an address. The - breakpoints manager presents all active breakpoints among all open programs and live traces. - Note that dead traces are not considered, and only the breakpoints at the present are - displayed, even if the user has stepped a trace backward. Breakpoints which map to the same - address in the same module, i.e., program image, and otherwise share the same attributes, are - grouped into a logical breakpoint. NOTE: The breakpoints window cannot display or - manipulate breakpoints from a target until that target is recorded into a trace. See the Static - Mappings window for the finer details of mapping traces to imported modules. In this - manner, breakpoints stored in Ghidra programs comprise the current breakpoint set, organized by - address — except for breakpoints outside a known imported module. The top table of the - provider displays logical breakpoints; the bottom table displays breakpoint locations.