Index: tests/zombies/string-perf/.gitignore
===================================================================
--- tests/zombies/string-perf/.gitignore	(revision fefd77adcd0a40d9ea468278e2369a92cfd9e773)
+++ tests/zombies/string-perf/.gitignore	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
@@ -1,2 +1,6 @@
 !Makefile
 perfexp-*
+make-corpus
+corpus-*.txt
+corpusx-*.txt
+measurement-*.csv
Index: tests/zombies/string-perf/Makefile
===================================================================
--- tests/zombies/string-perf/Makefile	(revision fefd77adcd0a40d9ea468278e2369a92cfd9e773)
+++ tests/zombies/string-perf/Makefile	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
@@ -5,5 +5,5 @@
 CFA = $(CFABUILD)/driver/cfa
 PERFFLAGS_CFA = -nodebug -O2
-PERFFLAGS_CXX = -DNDEBUG -O2
+PERFFLAGS_CXX = -DNDEBUG -O2 -Wl,--no-as-needed -ldl
 
 
@@ -37,5 +37,5 @@
 endef
 
-OPERATIONS=pta peq
+OPERATIONS=pta peq pbv pall pno
 ALLOCS=reuse fresh
 CFA_APILEVELS=hl ll
@@ -44,14 +44,25 @@
 
 ifneq ($(filter cfa,$(PLATFORMS)),)
-	CFA_PERFPROGS=$(call hyphCross5,perfexp-cfa,$(CFA_APILEVELS),$(OPERATIONS),$(CFA_SHARINGS),$(ALLOCS))
+    CFA_APIS=$(call hyphCross,$(CFA_APILEVELS),$(CFA_SHARINGS))
 endif
 
 ifneq ($(filter stl,$(PLATFORMS)),)
-	STL_PERFPROGS=$(call hyphCross3,perfexp-stl,$(OPERATIONS),$(ALLOCS))
+    STL_APIS=na-na
 endif
 
 ifneq ($(filter buhr94,$(PLATFORMS)),)
-	BUHR94_PERFPROGS=$(call hyphCross3,perfexp-buhr94,$(OPERATIONS),$(ALLOCS))
+    BUHR94_APIS=na-na
 endif
+
+APIS = $(CFA_APIS) $(STL_APIS) $(BUHR94_APIS)
+
+OPERATIONS_USING_ALLOCS=pta peq
+define enrichOperationsAllocs
+$(call hyphCross3,$(filter peq pta,$(OPERATIONS)),$(1),$(ALLOCS)) $(call hyphCross3,$(filter-out peq pta,$(OPERATIONS)),$(1),na)
+endef
+
+CFA_PERFPROGS=$(call hyphCross,perfexp-cfa,$(call enrichOperationsAllocs,$(CFA_APIS)))
+STL_PERFPROGS=$(call hyphCross,perfexp-stl,$(call enrichOperationsAllocs,$(STL_APIS)))
+BUHR94_PERFPROGS=$(call hyphCross,perfexp-buhr94,$(call enrichOperationsAllocs,$(BUHR94_APIS)))
 
 PERFPROGS = $(CFA_PERFPROGS) $(STL_PERFPROGS) $(BUHR94_PERFPROGS)
@@ -59,22 +70,63 @@
 all : $(PERFPROGS)
 
+PP_SPLIT := $(shell echo "${PERFPROGS}" | sed -e 's/ /\\n/g')
+echoPerfProgs:
+	echo -e '$(PP_SPLIT)'
 
+perfexp-%.o: API=$(call ucHyphProj,$@,2)
+perfexp-%.o: OPERATION=$(call ucHyphProj,$@,3)
+perfexp-%.o: CFA_APILEVEL=$(call ucHyphProj,$@,4)
+perfexp-%.o: CFA_SHARING=$(call ucHyphProj,$@,5)
+perfexp-%.o: ALLOC=$(call ucHyphProj,$@,6)
+perfexp-%.o: SCENARIO_SWITCH=-DIMPL_$(API)_$(CFA_APILEVEL)_$(CFA_SHARING) -DOP_$(OPERATION) -DALLOC_$(ALLOC)
 
-perfexp-cfa-%: CFA_APILEVEL=$(call ucHyphProj,$@,3)
-perfexp-cfa-%: OPERATION=$(call ucHyphProj,$@,4)
-perfexp-cfa-%: CFA_SHARING=$(call ucHyphProj,$@,5)
-perfexp-cfa-%: ALLOC=$(call ucHyphProj,$@,6)
-perfexp-cfa-%: prog.cfa $(LIBCFA)
-	$(CFA) $(PERFFLAGS_CFA) $< -o $@ -DIMPL_CFA_$(CFA_APILEVEL)_$(CFA_SHARING) -DOP_$(OPERATION) -DALLOC_$(ALLOC)
+perfexp-cfa-%.o: CMD=$(CFA) -c $(PERFFLAGS_CFA) $< -o $@ $(SCENARIO_SWITCH)
+perfexp-stl-%.o: CMD=$(CXX) -c -xc++ $(PERFFLAGS_CXX) $< -o $@ $(SCENARIO_SWITCH)
+perfexp-buhr94-%.o: CMD=$(CXX) -xc++ -c $(PERFFLAGS_CXX) $< -o $@ $(SCENARIO_SWITCH)
 
-perfexp-stl-%: OPERATION=$(call ucHyphProj,$@,3)
-perfexp-stl-%: ALLOC=$(call ucHyphProj,$@,4)
-perfexp-stl-%: prog.cfa
-	$(CXX) -xc++ $(PERFFLAGS_CXX) $< -o $@ -DIMPL_STL -DOP_$(OPERATION) -DALLOC_$(ALLOC)
+perfexp-cfa-peq-%.o: prog.cfa $(LIBCFA)
+	$(CMD)
+perfexp-cfa-pta-%.o: prog.cfa $(LIBCFA)
+	$(CMD)
+perfexp-cfa-pbv-%.o: prog-passbyval.cfa $(LIBCFA)
+	$(CMD)
+perfexp-cfa-pb%.o: prog-passbyX.cfa $(LIBCFA)
+	$(CMD)
+perfexp-cfa-pfi-%.o: prog-find.cfa $(LIBCFA)
+	$(CMD)
+perfexp-cfa-pall-%.o: prog-allocn.cfa $(LIBCFA)
+	$(CMD)
+perfexp-cfa-pno-%.o: prog-normalize.cfa $(LIBCFA)
+	$(CMD)
+perfexp-stl-peq-%.o: prog.cfa
+	$(CMD)
+perfexp-stl-pta-%.o: prog.cfa
+	$(CMD)
+perfexp-stl-pbv-%.o: prog-passbyval.cfa
+	$(CMD)
+perfexp-stl-pfi-%.o: prog-find.cfa
+	$(CMD)
+perfexp-stl-pall-%.o: prog-allocn.cfa
+	$(CMD)
+perfexp-stl-pno-%.o: prog-normalize.cfa
+	$(CMD)
+perfexp-buhr94-peq-%.o: prog.cfa buhr94-string.o buhr94-VbyteSM.o
+	$(CMD)
+perfexp-buhr94-pta-%.o: prog.cfa buhr94-string.o buhr94-VbyteSM.o
+	$(CMD)
+perfexp-buhr94-pta-%.o: prog-passbyval.cfa buhr94-string.o buhr94-VbyteSM.o
+	$(CMD)
+perfexp-buhr94-pall-%.o: prog-allocn.cfa buhr94-string.o buhr94-VbyteSM.o
+	$(CMD)
+perfexp-buhr94-pno-%.o: prog-normalize.cfa buhr94-string.o buhr94-VbyteSM.o
+	$(CMD)
 
-perfexp-buhr94-%.o: OPERATION=$(call ucHyphProj,$@,3)
-perfexp-buhr94-%.o: ALLOC=$(call ucHyphProj,$@,4)
-perfexp-buhr94-%.o: prog.cfa
-	$(CXX) -xc++ -c $(PERFFLAGS_CXX) $< -o $@ -DIMPL_BUHR94 -DOP_$(OPERATION) -DALLOC_$(ALLOC)
+# one of the pbx cases also needs to link with not_string_res.o (handling manually)
+perfexp-cfa-%: perfexp-cfa-%.o $(LIBCFA)
+	$(CFA) $(PERFFLAGS_CFA) $< -o $@  
+perfexp-stl-%: perfexp-stl-%.o $(LIBCFA)
+	$(CFA) $(PERFFLAGS_CFA) $< /lib/x86_64-linux-gnu/libstdc++.so.6 -o $@
+perfexp-buhr94-% : perfexp-buhr94-%.o buhr94-string.o buhr94-VbyteSM.o
+	$(CXX) $(PERFFLAGS_CXX) $^ -o $@
 
 buhr94-string.o:
@@ -83,7 +135,4 @@
 buhr94-VbyteSM.o:
 	$(CXX) -xc++ -c $(PERFFLAGS_CXX) ~/usys1/sm/string/StringSharing/src/VbyteSM.cc -o $@
-
-perfexp-buhr94-% : perfexp-buhr94-%.o buhr94-string.o buhr94-VbyteSM.o
-	$(CXX) $(PERFFLAGS_CXX) $^ -o $@
 
 clean:
@@ -104,2 +153,38 @@
 		done ; \
 	done
+#			printed=`./$$prog 10000 - 10 $$corpusbody` ; \
+
+CFA_EXPANSIONS=0.02 0.05 0.1 0.2 0.5 0.9
+
+measurement2: $(MEASURE)
+	tofile=measurement-`date '+%F--%H-%M-%S'`.csv ; \
+	for prog in $(MEASURE) ; do \
+	    for corpus in $(CORPORI) ; do \
+			for expansion in $(CFA_EXPANSIONS) ; do \
+				corpusbody= ; \
+				echo ./$$prog 1000 1.006 $$expansion 10 \`cat $$corpus\` ; \
+			done ; \
+		done ; \
+	done ; \
+	echo $$tofile ; \
+	for prog in $(MEASURE) ; do \
+	    for corpus in $(CORPORI) ; do \
+			for expansion in $(CFA_EXPANSIONS) ; do \
+				corpusbody=`cat $$corpus` ; \
+				printed=`./$$prog 1000 1.006 $$expansion 10 $$corpusbody` ; \
+				echo $$prog,$$corpus,$$expansion,$$printed  >>  $$tofile ; \
+				echo $$prog,$$corpus,$$expansion,$$printed  ; \
+			done ; \
+		done ; \
+	done
+
+measurement3: $(MEASURE)
+	for prog in $(MEASURE) ; do \
+	    for corpus in $(CORPORI) ; do \
+			for expansion in $(CFA_EXPANSIONS) ; do \
+				corpusbody=`cat $$corpus` ; \
+				LD_PRELOAD=~/plg2/mubeen-stat-shim/malloc/mallocWrappers.so ./$$prog 1000 1.006 $$expansion 1 $$corpusbody ; \
+				mv preload_dump.txt preload_dump--qrun1--$$corpus--expansion-$$expansion.txt ; \
+			done ; \
+		done ; \
+	done
Index: tests/zombies/string-perf/corpori-about.txt
===================================================================
--- tests/zombies/string-perf/corpori-about.txt	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
+++ tests/zombies/string-perf/corpori-about.txt	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
@@ -0,0 +1,9 @@
+corpus-100-* are for general-purpose mixed tests
+corpus-1-* are single-item corpori, for running exact-length variations  (made by hand with a clipboard)
+corpusx-* are purposed for getting started with the "normalize" test, using randomness to have rare -s, yet awkwardly avoiding SSO with min-length 16
+          x means 1% -s
+
+IIRC, ./make-corpus 100 20           DROVE    corpus-100-20-1.txt
+ergo, ./make-corpus 100 5 15 0.01    IS FOR   corpusx-100-20-1.txt
+
+./make-corpus 100 5 15 0.01  >  corpusx-100-20-1.txt
Index: tests/zombies/string-perf/make-corpus.cfa
===================================================================
--- tests/zombies/string-perf/make-corpus.cfa	(revision fefd77adcd0a40d9ea468278e2369a92cfd9e773)
+++ tests/zombies/string-perf/make-corpus.cfa	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
@@ -3,4 +3,10 @@
 #include <limits.h>
 #include <unistd.h>
+#include <string.h>
+
+// U(0,1)
+static double U() {
+    return (double)rand() / (double)INT_MAX;
+}
 
 // generate random draws from a geometric distribution of the given mean
@@ -14,14 +20,15 @@
 static int nextGeoRand() {
     // ret = ⌊ln(U)/ln(1−p)⌋ where U ~ U(0, 1)
-    double U = (double)rand() / (double)INT_MAX;
-    return 1 + (int) (log(U) / denom);
+    return 1 + (int) (log(U()) / denom);
 }
 
-// write a randomly generated alphabetic string whose length is drawn from above distribution
-static void emit1() {
-    int lim = nextGeoRand();
+// write a randomly generated alphabetic string whose length is adjused from a draw of the above distribution
+static void emit1( int offset, double mcfreq, char mchar ) {
+    int lim = offset + nextGeoRand();
     // printf("==%d\n", lim);
     for (i; lim) {
-        char emit = 'a' + (rand() % ('z'-'a'));
+        char emit;
+        if (U() < mcfreq) emit = mchar;
+        else emit = 'a' + (rand() % ('z'-'a'));
         printf("%c", emit);
     }
@@ -29,19 +36,52 @@
 }
 
-// usage: ./make-corpus toGen mean
+// usage: ./make-corpus toGen mean [offset=0] [mcfreq=0.0] [mchar='-']
+//
+// Outputs alphabetic (plus magic-char) strings, one per line.
+// toGen: number of strings (lines)
+// 
+// generated length ~  offset + geo(mean)
+//                  >= 1
+//
+// offset=0,  mean=1:  constant length 1
+// offset=0,  mean=2:  lengths go like number of coin tosses it takes to get heads
+// offset=0,  mean=6:  lengths go like number of cube die rolls it takes to get :::
+// offset=15, mean=1:  constant length 16
+// offset=15, mean=2:  population's minimum is 16 and mean is 17
+//
+// Magic Char (mc) does not affect these lengths.  Any mc occurrence replaces an alphabetic char.
+// mcfreq: (in [0,1]) expected fraction of the characters output that are mchar
+//
 int main(int argc, char ** argv) {
-    assert(argc == 3);
 
-    int toGen = atoi(argv[1]);
-    assert(toGen > 0);
-    assert(toGen < 1000000);
+    int toGen;
+    int mean;
+    int offset = 0;
+    double mcfreq = 0.0;
+    char mchar = '-';
 
-    int mean = atoi(argv[2]);
-    assert(mean > 0);
-    assert(mean < 1000);
+    assert(argc >= 3 && argc <= 6);
+    switch(argc) {
+        case 6:
+            assert(strlen(argv[5]) == 0);
+            mchar = argv[5][0];
+        case 5:
+            mcfreq = atof(argv[4]);
+            assert(mcfreq >= 0.0 && mcfreq <= 1.0);
+        case 4:
+            offset = atoi(argv[3]);
+            assert(offset >= 0 && offset < 10000);
+        default:
+            mean = atoi(argv[2]);
+            assert(mean > 0);
+            assert(mean < 1000);
+            toGen = atoi(argv[1]);
+            assert(toGen > 0);
+            assert(toGen < 1000000);
+    }
 
     initialize(mean);
     for( i; toGen ) {
-        emit1();
+        emit1(offset, mcfreq, mchar);
     }
 }
Index: tests/zombies/string-perf/make-flamegraph.sh
===================================================================
--- tests/zombies/string-perf/make-flamegraph.sh	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
+++ tests/zombies/string-perf/make-flamegraph.sh	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# $1 is SUT id     : stl-pall-na-na-na
+# $2 is CLI middle : 1000 1.006 0.1 -w 10000000
+# $3 is corpus id  : -1-500-1  (goes with corpus-1-500-1.txt)
+#
+# usage for that example:   ./make-flamegraph.sh cfa-pall-ll-share-na '1000 1.006 0.1 -w 10000000' -1-500-1
+#
+# another
+# $3 is coprus id  : x-100-20-1  (goes with corpusx-100-20-1.txt)
+
+SLUG=perf--$1--${2//[ .]/-}--$3
+
+perf record --call-graph dwarf -m16M ~/plg2/cfa2/cfa-cc/tests/zombies/string-perf/perfexp-$1 $2 `cat ~/plg2/cfa2/cfa-cc/tests/zombies/string-perf/corpus$3.txt`
+
+mv perf.data $SLUG.data
+perf script -i $SLUG.data > $SLUG.perf
+../flamegraph/FlameGraph/stackcollapse-perf.pl $SLUG.perf > $SLUG.folded
+../flamegraph/FlameGraph/flamegraph.pl $SLUG.folded > $SLUG.svg
+cp $SLUG.svg ~/plg2/flames
Index: tests/zombies/string-perf/not_string_res.cfa
===================================================================
--- tests/zombies/string-perf/not_string_res.cfa	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
+++ tests/zombies/string-perf/not_string_res.cfa	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
@@ -0,0 +1,5 @@
+#include <string_res.hfa>
+#include "not_string_res.hfa"
+
+void ?{}( not_string_res & this, const string_res & src, StrResInitMode, size_t start, size_t end ) {}
+void ^?{}( not_string_res & this ) {}
Index: tests/zombies/string-perf/not_string_res.hfa
===================================================================
--- tests/zombies/string-perf/not_string_res.hfa	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
+++ tests/zombies/string-perf/not_string_res.hfa	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
@@ -0,0 +1,6 @@
+struct not_string_res {
+    size_t junk[7];
+};
+
+void ?{}( not_string_res & this, const string_res & src, StrResInitMode, size_t start, size_t end );
+void ^?{}( not_string_res & this );
Index: tests/zombies/string-perf/pbx-correctness-demos.cfa
===================================================================
--- tests/zombies/string-perf/pbx-correctness-demos.cfa	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
+++ tests/zombies/string-perf/pbx-correctness-demos.cfa	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
@@ -0,0 +1,206 @@
+// see also C++ investigation in ~/plg2/cfa2/mycode/string/raii/ctor-calls.cpp
+
+#include <string.hfa>
+#include <string_res.hfa>
+
+/*
+call_substrOfPart:
+    In all cases, of both HL and LL, there is a temporary object.
+    It represents narrowing the range, from full-string, to [1..3].
+    The only matter to control is whether that temporary shares edits with the string from which it was created.
+    In HL, two knobs control it, and the 
+*/
+
+void calltest_HL() {
+
+    #define HELPER_BODY(param) \
+        sout | "early in helper with " | param; \
+        param[0] = '+'; \
+        sout | "late in helper with " | param;
+
+    void helper1( string   q ) { HELPER_BODY(q) }
+    void helper2( string & q ) { HELPER_BODY(q) }
+    #undef HELPER_BODY
+
+    string fred;
+
+
+    sout | "===";
+    sout | "HL: substring of part";
+
+    sout | "---";
+    // Calling a by-val function, the only way it supports, in which it gets a private logical copy.
+    fred = "abcd";
+    sout | "before helper with " | fred;
+    helper1( fred(1,3) );
+    sout | "after helper with " | fred;
+
+    sout | "---";
+    // Calling a by-ref function, sys-style, in which we want to gets its changes as side effects.
+    fred = "abcd";
+    sout | "before helper with " | fred;
+    helper2( fred(1,3) );
+    sout | "after helper with " | fred;
+
+    sout | "---";
+    // Calling a by-ref function, trans-style, in which we give it a logical copy, to prevent it from pulluting our gold one.
+    fred = "abcd";
+    sout | "before helper with " | fred;
+    helper2( (string){ fred(1,3) } );
+    sout | "after helper with " | fred;
+
+
+    sout | "===";
+    sout | "HL: substring of whole";
+
+    sout | "---";
+    // Calling a by-val function, the only way it supports, in which it gets a private logical copy.
+    fred = "abcd";
+    sout | "before helper with " | fred;
+    helper1( fred(0,4) );
+    sout | "after helper with " | fred;
+
+    sout | "---";
+    // Calling a by-ref function, sys-style, in which we want to gets its changes as side effects.
+    fred = "abcd";
+    sout | "before helper with " | fred;
+    helper2( fred(0,4) );
+    sout | "after helper with " | fred;
+
+    sout | "---";
+    // Calling a by-ref function, trans-style, in which we give it a logical copy, to prevent it from pulluting our gold one.
+    fred = "abcd";
+    sout | "before helper with " | fred;
+    helper2( (string){ fred(0,4) } );
+    sout | "after helper with " | fred;
+
+
+
+    sout | "===";
+    sout | "HL: whole original string";
+
+    sout | "---";
+    // Calling a by-val function, the only way it supports, in which it gets a private logical copy.
+    fred = "abcd";
+    sout | "before helper with " | fred;
+    helper1( fred );
+    sout | "after helper with " | fred;
+
+    sout | "---";
+    // Calling a by-ref function, sys-style, in which we want to gets its changes as side effects.
+    fred = "abcd";
+    sout | "before helper with " | fred;
+    helper2( fred );
+    sout | "after helper with " | fred;
+
+    sout | "---";
+    // Calling a by-ref function, trans-style, in which we give it a logical copy, to prevent it from pulluting our gold one.
+    fred = "abcd";
+    sout | "before helper with " | fred;
+    helper2( (string){ fred } );
+    sout | "after helper with " | fred;
+}
+
+void calltest_LL() {
+
+    #define HELPER_BODY(param) \
+        sout | "early in helper with " | param; \
+        assignAt(param, 0, '+'); \
+        sout | "late in helper with " | param;
+
+    void helper1( string_res & q ) { HELPER_BODY(q) } // arg should always be a temporary constructed with COPY_VALUE
+    void helper2( string_res & q ) { HELPER_BODY(q) } // arg can refer to whatever you want
+    #undef HELPER_BODY
+
+    /*
+    In LL, both functions are translated to the same thing.
+    "The only allowed call of #1" becomes respecting the restriction of the comment at helper1.
+    A sys call of #2 is different from the only allowed call of #1.
+    A trans call of #2 is the same as the only allowed call of #1.
+    */
+
+    string_res fred;
+
+    sout | "===";
+    sout | "LL: substring of part";
+
+    sout | "---";
+    // Calling a by-val function, the only way it supports, in which it gets a private logical copy.
+    fred = "abcd";
+    sout | "before helper with " | fred;
+    helper1( (string_res){ fred, COPY_VALUE, 1, 3 } );
+    sout | "after helper with " | fred;
+
+    sout | "---";
+    // Calling a by-ref function, sys-style, in which we want to gets its changes as side effects.
+    fred = "abcd";
+    sout | "before helper with " | fred;
+    helper2( (string_res){ fred, SHARE_EDITS, 1, 3 } );
+    sout | "after helper with " | fred;
+
+    sout | "---";
+    // Calling a by-ref function, trans-style, in which we give it a logical copy, to prevent it from pulluting our gold one.
+    fred = "abcd";
+    sout | "before helper with " | fred;
+    helper2( (string_res){ fred, COPY_VALUE, 1, 3 } );
+    sout | "after helper with " | fred;
+
+
+    sout | "===";
+    sout | "LL: substring of whole";
+
+    sout | "---";
+    // Calling a by-val function, the only way it supports, in which it gets a private logical copy.
+    fred = "abcd";
+    sout | "before helper with " | fred;
+    helper1( (string_res){ fred, COPY_VALUE, 0, 4 } );
+    sout | "after helper with " | fred;
+
+    sout | "---";
+    // Calling a by-ref function, sys-style, in which we want to gets its changes as side effects.
+    fred = "abcd";
+    sout | "before helper with " | fred;
+    helper2( (string_res){ fred, SHARE_EDITS, 0, 4 } );
+    sout | "after helper with " | fred;
+
+    sout | "---";
+    // Calling a by-ref function, trans-style, in which we give it a logical copy, to prevent it from pulluting our gold one.
+    fred = "abcd";
+    sout | "before helper with " | fred;
+    helper2( (string_res){ fred, COPY_VALUE, 0, 4 } );
+    sout | "after helper with " | fred;
+
+
+    sout | "===";
+    sout | "LL: whole original string";
+
+    sout | "---";
+    // Calling a by-val function, the only way it supports, in which it gets a private logical copy.
+    fred = "abcd";
+    sout | "before helper with " | fred;
+    helper1( (string_res){ fred, COPY_VALUE } );
+    sout | "after helper with " | fred;
+
+    sout | "---";
+    // Calling a by-ref function, sys-style, in which we want to gets its changes as side effects.
+    fred = "abcd";
+    sout | "before helper with " | fred;
+    helper2( fred );
+    sout | "after helper with " | fred;
+
+    sout | "---";
+    // Calling a by-ref function, trans-style, in which we give it a logical copy, to prevent it from pulluting our gold one.
+    fred = "abcd";
+    sout | "before helper with " | fred;
+    helper2( (string_res){ fred, COPY_VALUE } );
+    sout | "after helper with " | fred;
+}
+
+
+
+int main() {
+
+    calltest_HL();
+    calltest_LL();
+
+}
Index: tests/zombies/string-perf/pbx-correctness-demos.cpp
===================================================================
--- tests/zombies/string-perf/pbx-correctness-demos.cpp	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
+++ tests/zombies/string-perf/pbx-correctness-demos.cpp	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
@@ -0,0 +1,102 @@
+// see also C++ investigation in ~/plg2/cfa2/mycode/string/raii/ctor-calls.cpp
+
+#include <string>
+#include <iostream>
+
+using namespace std;
+
+#define HELPER_BODY(param) \
+    cout << "early in helper with " << param << endl; \
+    param[0] = '+'; \
+    cout << "late in helper with " << param << endl;
+
+void helper1( string   q ) { HELPER_BODY(q) }
+void helper2( string & q ) { HELPER_BODY(q) }
+#undef HELPER_BODY
+
+
+void calltest_HL() {
+
+    string fred;
+
+
+    cout << "===" << endl;
+    cout << "HL: substring of part" << endl;
+
+    cout << "---" << endl;
+    // Calling a by-val function, the only way it supports, in which it gets a private logical copy.
+    fred = "abcd";
+    cout << "before helper with " << fred << endl;
+    helper1( fred.substr(1,3) );
+    cout << "after helper with " << fred << endl;
+
+    cout << "---" << endl;
+    // Calling a by-ref function, catching side effects the only place STL-string gives them, in an explicit copy.
+    fred = "abcd";
+    cout << "before helper with " << fred << endl;
+    {
+        string fred_sub = fred.substr(1,3);
+        helper2( fred_sub );
+        cout << "after helper with temp having " << fred_sub << endl;
+    }
+    cout << "after helper with original having " << fred << endl;
+
+
+    cout << "===" << endl;
+    cout << "HL: substring of whole" << endl;
+
+    cout << "---" << endl;
+    // Calling a by-val function, the only way it supports, in which it gets a private logical copy.
+    fred = "abcd";
+    cout << "before helper with " << fred << endl;
+    helper1( fred.substr(0,4) );
+    cout << "after helper with " << fred << endl;
+
+    cout << "---" << endl;
+    // Calling a by-ref function, catching side effects the only place STL-string gives them, in an explicit copy.
+    fred = "abcd";
+    cout << "before helper with " << fred << endl;
+    {
+        string fred_sub = fred.substr(0,4);
+        helper2( fred_sub );
+        cout << "after helper with temp having " << fred_sub << endl;
+    }
+    cout << "after helper with original having " << fred << endl;
+
+
+
+    cout << "===" << endl;
+    cout << "HL: whole original string" << endl;
+
+    cout << "---" << endl;
+    // Calling a by-val function, the only way it supports, in which it gets a private logical copy.
+    fred = "abcd";
+    cout << "before helper with " << fred << endl;
+    helper1( fred );
+    cout << "after helper with " << fred << endl;
+
+    cout << "---" << endl;
+    // Calling a by-ref function, sys-style, in which we want to gets its changes as side effects.
+    fred = "abcd";
+    cout << "before helper with " << fred << endl;
+    helper2( fred );
+    cout << "after helper with " << fred << endl;
+
+    cout << "---" << endl;
+    // Calling a by-ref function, trans-style, in which we give it a logical copy, to prevent it from pulluting our gold one; copy needs explicit variable.
+    fred = "abcd";
+    cout << "before helper with " << fred << endl;
+    {
+        string fred_cpy = fred;
+        helper2( fred_cpy );
+    }
+    cout << "after helper with " << fred << endl;
+}
+
+
+
+int main() {
+
+    calltest_HL();
+
+}
Index: tests/zombies/string-perf/prog-allocn.cfa
===================================================================
--- tests/zombies/string-perf/prog-allocn.cfa	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
+++ tests/zombies/string-perf/prog-allocn.cfa	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
@@ -0,0 +1,218 @@
+#if defined IMPL_STL_NA_NA
+  #define IMPL_STL
+#endif
+
+#if defined IMPL_BUHR94_NA_NA
+  #define IMPL_BUHR94
+#endif
+
+#if defined IMPL_STL
+  #include <string>
+  #include <iostream>
+  #include <cstdio>
+  using namespace std;
+  #define IMPL_CXX
+
+#elif defined IMPL_CFA_HL_SHARE
+  #define IMPL_CFA_HL
+  #define IMPL_CFA
+
+#elif defined IMPL_CFA_LL_SHARE
+  #define IMPL_CFA_LL
+  #define IMPL_CFA
+
+#elif defined IMPL_CFA_HL_NOSHARE
+  #define IMPL_CFA_HL
+  #define CFA_NOSHARE
+  #define IMPL_CFA
+
+#elif defined IMPL_CFA_LL_NOSHARE
+  #define IMPL_CFA_LL
+  #define CFA_NOSHARE
+  #define IMPL_CFA
+
+#elif defined IMPL_BUHR94
+  #include <iostream>
+  #include <cstdio>
+  #include "/u0/mlbrooks/usys1/sm/string/StringSharing/src/string.h"
+  #define IMPL_CXX
+
+#else
+  #error Bad IMPL_
+#endif
+
+
+#if defined IMPL_CFA_HL
+  #include <string.hfa>
+  extern void TUNING_set_string_heap_liveness_threshold(double);  // in string_res.hfa
+#elif defined IMPL_CFA_LL
+  #include <string_res.hfa>
+#endif
+
+#if defined CFA_NOSHARE
+  #include <string_sharectx.hfa>
+  #define STRING_SHARING_CONTROL \
+    string_sharectx c = { NO_SHARING };
+#else 
+  #define STRING_SHARING_CONTROL
+#endif
+
+#if defined IMPL_CFA
+  #include <math.hfa>
+  extern "C" {
+    void malloc_stats( void );
+  }
+#elif defined IMPL_CXX
+  #include <algorithm>
+  using std::min;
+  #include <malloc.h>
+#endif
+
+#include <time.h>
+#include <stdlib.h> // atoi
+#include <string.h> // strlen, only during setup
+
+#if defined IMPL_STL || defined IMPL_BUHR94
+    #define PRINT(s) std::cout << s << std::endl
+#elif defined IMPL_CFA_HL || defined IMPL_CFA_LL
+    #define PRINT(s) sout | s;
+#else
+    #error Unhandled print case
+#endif
+
+#if defined IMPL_CFA_LL
+    #define STRING_T string_res
+    #define ASSIGN_CHAR(str, idx, val) assignAt(str, idx, val)
+#else
+    #define STRING_T string
+    #define ASSIGN_CHAR(str, idx, val) str[idx] = val
+#endif
+
+double meanLen(int N, char ** strings) {
+    int totalLen = 0;
+    for (int i = 0 ; i < N; i ++) {
+        totalLen += strlen(strings[i]);
+    }
+    return (double)totalLen / (double)N;
+}
+
+volatile int checkthis = 0;
+#define MAYBE( op ) if (checkthis) { op; }
+
+int corpuslen = 0;
+char ** corpus = (char**) 0;
+size_t corpus_next_pos = 0;
+
+double repsPerLevel;
+double repBalance = 0.0000001;
+
+clock_t start, endTarget, end_actual;
+size_t allocationCountTarget = 0;
+
+size_t allocationCountActual = 0;
+//size_t allocationBytesActual = 0;
+
+void helper( int depth ) {
+
+    if (depth == 0) return;
+
+    corpus_next_pos += 1;
+    corpus_next_pos %= corpuslen;
+
+    STRING_T q = corpus[corpus_next_pos];
+//    ASSIGN_CHAR(q, 0, '@');    // Turns out my implementation is slow at this step.  A separate test could work it.  It's inessential to the allocation test, given the assumption that both string reps allocate eagerly in their heaps from a constant.  In the STL, that assumption is upheld by my observation that commenting out this line didn't speed it up.  In CFA-share, I know it to be true of the implementation.
+
+    allocationCountActual += 1;
+//    allocationBytesActual += q`len;
+
+//    if (depth > 0) {
+
+        repBalance += repsPerLevel;
+        int curRepLimit = repBalance;
+        repBalance -= curRepLimit;
+
+        for ( int i = 0 ;  i < curRepLimit;  i++ ) {
+            if ((allocationCountActual+1) % allocationCountTarget == 0 && clock() > endTarget) return;
+            helper(depth-1);
+        }
+//    }
+
+    MAYBE(PRINT(q));
+}
+
+int main( int argc, char ** argv ) {
+
+    STRING_SHARING_CONTROL
+
+
+    const char * usage_args[] = {"Depth RepsPerLevel ExpansionThreshold    ExecTimeSecs   Corpus...",
+                                 "Depth RepsPerLevel ExpansionThreshold -w WorkAllocCount Corpus..."};
+    const int static_arg_posns = 5;
+    int used_arg_posns = static_arg_posns;
+
+    int launchDepth = -1;
+    double expansionThreshold = -1.0;
+    int execTimeSecs = -1;
+
+    switch (min(argc, static_arg_posns)) {
+      case 5: if ( strcmp(argv[4], "-w") == 0 ) {
+                used_arg_posns ++;
+                allocationCountTarget = atoi(argv[5]);
+              } else {
+                execTimeSecs = atoi(argv[4]);
+              }
+      case 4: expansionThreshold = atof(argv[3]);
+      case 3: repsPerLevel = atof(argv[2]);
+      case 2: launchDepth = atoi(argv[1]);
+    }
+
+    corpuslen = argc - used_arg_posns;
+    corpus = argv + used_arg_posns;
+
+    if (launchDepth < 1 || repsPerLevel < 1.0 || (execTimeSecs < 1 && allocationCountTarget < 1) || corpuslen < 1 ||
+        (expansionThreshold != -1.0 && (expansionThreshold <= 0.0 || expansionThreshold >= 1.0))) {
+      for (int u = 0; u < sizeof(usage_args) / sizeof(*usage_args); u++) {
+        printf("usage: %s %s\n", argv[0], usage_args[u]);
+      }
+      printf("output:\nxxx,corpusItemCount,corpusMeanLenChars,allocationCountActual,execTimeActualSec,topIters\n");
+      exit(1);
+    }
+
+    if (expansionThreshold != -1.0 ) {
+      #if defined IMPL_CFA
+      TUNING_set_string_heap_liveness_threshold(expansionThreshold);
+      #else
+      printf("cannot set expansion threshold on non-CFA implementation");
+      exit(1);
+      #endif
+    }
+
+    double meanCorpusLen = meanLen(corpuslen, corpus);
+
+    // time driven experiment: re-check time every 10000 allocations
+    if (execTimeSecs > 0) allocationCountTarget = 10000;
+
+    start = clock();
+    endTarget = start + CLOCKS_PER_SEC * max(0, execTimeSecs);
+
+    size_t top_iters = 0;
+
+    for(;;) {
+            #if defined OP_PALL
+                helper( launchDepth );
+            #else
+                #error Bad OP_
+            #endif
+
+            top_iters++;
+
+            if ((allocationCountActual+1) % allocationCountTarget == 0 && clock() > endTarget) break;
+    }
+    end_actual = clock();
+    double elapsed = ((double) (end_actual - start)) / CLOCKS_PER_SEC;
+    printf("xxx,%d,%f,%ld,%f,%ld\n", corpuslen, meanCorpusLen, allocationCountActual, elapsed, top_iters);
+
+    // malloc_stats();
+
+    return 0;
+}
Index: tests/zombies/string-perf/prog-find.cfa
===================================================================
--- tests/zombies/string-perf/prog-find.cfa	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
+++ tests/zombies/string-perf/prog-find.cfa	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
@@ -0,0 +1,184 @@
+#if defined IMPL_STL_NA_NA
+  #define IMPL_STL
+#endif
+
+#if defined IMPL_BUHR94_NA_NA
+  #define IMPL_BUHR94
+#endif
+
+#if defined IMPL_STL
+  #include <string>
+  #include <iostream>
+  #include <cstdio>
+  using namespace std;
+  #define IMPL_CXX
+
+#elif defined IMPL_CFA_HL_SHARE
+  #define IMPL_CFA_HL
+  #define IMPL_CFA
+
+#elif defined IMPL_CFA_LL_SHARE
+  #define IMPL_CFA_LL
+  #define IMPL_CFA
+
+#elif defined IMPL_CFA_HL_NOSHARE
+  #define IMPL_CFA_HL
+  #define CFA_NOSHARE
+  #define IMPL_CFA
+
+#elif defined IMPL_CFA_LL_NOSHARE
+  #define IMPL_CFA_LL
+  #define CFA_NOSHARE
+  #define IMPL_CFA
+
+#elif defined IMPL_BUHR94
+  #include <iostream>
+  #include <cstdio>
+  #include "/u0/mlbrooks/usys1/sm/string/StringSharing/src/string.h"
+  #define IMPL_CXX
+
+#else
+  #error Bad IMPL_
+#endif
+
+
+#if defined IMPL_CFA_HL
+  #include <string.hfa>
+#elif defined IMPL_CFA_LL
+  #include <string_res.hfa>
+  #include "not_string_res.hfa"
+#endif
+
+#if defined CFA_NOSHARE
+  #include <string_sharectx.hfa>
+  #define STRING_SHARING_CONTROL \
+    string_sharectx c = { NO_SHARING };
+#else 
+  #define STRING_SHARING_CONTROL
+#endif
+
+#if defined IMPL_CFA
+  #include <math.hfa>
+  extern "C" {
+    void malloc_stats( void );
+  }
+#elif defined IMPL_CXX
+  #include <algorithm>
+  using std::min;
+  #include <malloc.h>
+#endif
+
+#include <time.h>
+#include <stdlib.h> // atoi
+#include <string.h> // strlen, only during setup
+#include <limits.h> // LONG_MAX
+
+#if defined IMPL_STL || defined IMPL_BUHR94
+    #define PRINT(s) std::cout << s << std::endl
+#elif defined IMPL_CFA_HL || defined IMPL_CFA_LL
+    #define PRINT(s) sout | s;
+#else
+    #error Unhandled print case
+#endif
+
+#if defined IMPL_CFA_LL
+    #define STRING_T string_res
+    #define ASSIGN_CHAR(str, idx, val) assignAt(str, idx, val)
+#else
+    #define STRING_T string
+    #define ASSIGN_CHAR(str, idx, val) str[idx] = val
+#endif
+
+double meanLen(int N, char ** strings) {
+    int totalLen = 0;
+    for (int i = 0 ; i < N; i ++) {
+        totalLen += strlen(strings[i]);
+    }
+    return (double)totalLen / (double)N;
+}
+
+volatile int checkthis = 0;
+#define MAYBE( op ) if (checkthis) { op; }
+
+
+int main( int argc, char ** argv ) {
+
+    STRING_SHARING_CONTROL
+
+
+    const char * usage_args[] = {"(Ignored)    ExecTimeSecs   Corpus...",
+                                 "(Ignored) -w WorkAllocCount Corpus..."};
+
+    const int static_arg_posns = 3;
+    int used_arg_posns = static_arg_posns;
+
+    int execTimeSecs = -1;
+
+    long int iterationCountTarget = -1;
+
+    switch (min(argc, static_arg_posns)) {
+      case 3: if ( strcmp(argv[2], "-w") == 0 ) {
+                used_arg_posns ++;
+                execTimeSecs = 0;
+                iterationCountTarget = atoi(argv[3]);
+              } else {
+                execTimeSecs = atoi(argv[2]);
+                iterationCountTarget = LONG_MAX;
+              }
+    }
+
+    int corpuslen = argc - used_arg_posns;
+    char ** corpus = argv + used_arg_posns;
+
+    if ((execTimeSecs < 1 && iterationCountTarget < 1)  || corpuslen < 1) {
+      for (int u = 0; u < sizeof(usage_args) / sizeof(*usage_args); u++) {
+        printf("usage: %s %s\n", argv[0], usage_args[u]);
+      }
+      printf("output:\nxxx,corpusItemCount,corpusMeanLenChars,callDoneActualCount,execTimeActualSec\n");
+      exit(1);
+    }
+
+    double meanCorpusLen = meanLen(corpuslen, corpus);
+
+    clock_t start, end_target, end_actual;
+
+    STRING_T corpus_imported[corpuslen];
+
+    for (int i = 0; i < corpuslen; i++) {
+        corpus_imported[i] = corpus[i];
+        // if the callee ever modifies, then we will drive GCs, which will visit the entire corpus
+    }
+
+    start = clock();
+    if (execTimeSecs != 0) {
+      end_target = start + CLOCKS_PER_SEC * execTimeSecs;
+    } else {
+      end_target = start + CLOCKS_PER_SEC * 3600;
+    }
+
+    unsigned int t = 0;
+    for ( ; t < iterationCountTarget && ((t+1) % 10000 != 0 || clock() < end_target) ; t += 1 ) {
+            #if defined OP_PFI
+              size_t foundAt =
+                #if defined IMPL_CFA
+                   find( corpus_imported[t % corpuslen], ' ')
+                #elif defined IMPL_STL
+                   corpus_imported[t % corpuslen].find(' ')
+                #else
+                  #error Bad IMPL
+                #endif
+                ;
+            #else
+              #error Bad OP_
+            #endif
+            MAYBE( PRINT( foundAt ) );
+    }
+    end_actual = clock();
+    unsigned int callsDone = t;
+    double elapsed = ((double) (end_actual - start)) / CLOCKS_PER_SEC;
+    printf("xxx,%d,%f,%d,%f\n", corpuslen, meanCorpusLen, t, elapsed);
+
+    // malloc_stats();
+
+    return 0;
+}
Index: tests/zombies/string-perf/prog-normalize.cfa
===================================================================
--- tests/zombies/string-perf/prog-normalize.cfa	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
+++ tests/zombies/string-perf/prog-normalize.cfa	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
@@ -0,0 +1,264 @@
+#if defined IMPL_STL_NA_NA
+  #define IMPL_STL
+#endif
+
+#if defined IMPL_BUHR94_NA_NA
+  #define IMPL_BUHR94
+#endif
+
+#if defined IMPL_STL
+  #include <string>
+  #include <iostream>
+  #include <cstdio>
+  using namespace std;
+  #define IMPL_CXX
+
+#elif defined IMPL_CFA_HL_SHARE
+  #define IMPL_CFA_HL
+  #define IMPL_CFA
+
+#elif defined IMPL_CFA_LL_SHARE
+  #define IMPL_CFA_LL
+  #define IMPL_CFA
+
+#elif defined IMPL_CFA_HL_NOSHARE
+  #define IMPL_CFA_HL
+  #define CFA_NOSHARE
+  #define IMPL_CFA
+
+#elif defined IMPL_CFA_LL_NOSHARE
+  #define IMPL_CFA_LL
+  #define CFA_NOSHARE
+  #define IMPL_CFA
+
+#elif defined IMPL_BUHR94
+  #include <iostream>
+  #include <cstdio>
+  #include "/u0/mlbrooks/usys1/sm/string/StringSharing/src/string.h"
+  #define IMPL_CXX
+
+#else
+  #error Bad IMPL_
+#endif
+
+
+#if defined IMPL_CFA_HL
+  #include <string.hfa>
+#elif defined IMPL_CFA_LL
+  #include <string_res.hfa>
+#endif
+
+#if defined CFA_NOSHARE
+  #include <string_sharectx.hfa>
+  #define STRING_SHARING_CONTROL \
+    string_sharectx c = { NO_SHARING };
+#else 
+  #define STRING_SHARING_CONTROL
+#endif
+
+#if defined IMPL_CFA
+  #include <math.hfa>
+  extern "C" {
+    void malloc_stats( void );
+  }
+#elif defined IMPL_CXX
+  #include <algorithm>
+  using std::min;
+  #include <malloc.h>
+#endif
+
+#include <time.h>
+#include <stdlib.h> // atoi
+#include <string.h> // strlen, only during setup
+
+#if defined IMPL_STL || defined IMPL_BUHR94
+    #define PRINT(s) std::cout << s << std::endl
+#elif defined IMPL_CFA_HL || defined IMPL_CFA_LL
+    #define PRINT(s) sout | s;
+#else
+    #error Unhandled print case
+#endif
+
+#if defined IMPL_CFA_LL
+    #define STRING_T string_res
+    #define ASSIGN_CHAR(str, idx, val) assignAt(str, idx, val)
+#else
+    #define STRING_T string
+    #define ASSIGN_CHAR(str, idx, val) str[idx] = val
+#endif
+
+#if defined IMPL_CFA
+    #define LEN(str) size(str)
+#elif defined IMPL_STL 
+    #define LEN(str) str.length()
+#else
+    #error need LEN definition for this IMPL_
+#endif
+
+double meanLen(int N, char ** strings) {
+    int totalLen = 0;
+    for (int i = 0 ; i < N; i ++) {
+        totalLen += strlen(strings[i]);
+    }
+    return (double)totalLen / (double)N;
+}
+
+volatile int checkthis = 0;
+#define MAYBE( op ) if (checkthis) { op; }
+
+// setup, not being timed, nor judged for aesthetics
+void makeSuperCorpus( STRING_T & target, int corpusLen, char ** corpus, int scSize, char scDelimiter ) {
+    char delimiterStr[2] = { scDelimiter, '\0' };
+    for ( int i = 0;  i < scSize; i++ ) {
+        target += corpus[ i % corpusLen ];
+        target += delimiterStr;
+    }
+}
+
+// the function at issue
+
+#if defined IMPL_CFA_HL
+void processThing( string & thing, char magicChar ) {
+    MAYBE(PRINT(thing));
+    size_t foundPos;
+    for (;;) {
+        size_t size_thing = size(thing);
+        foundPos = find(thing, magicChar);
+        if( foundPos == size_thing ) break;
+        string mcOccur = thing(foundPos, foundPos+1)`shareEdits;
+        mcOccur = "";
+    }
+    return thing;
+}
+
+#elif defined IMPL_CFA_LL
+void processThing( string_res & thing, char magicChar ) {
+    MAYBE(PRINT(thing));
+    size_t foundPos;
+    for (;;) {
+        size_t size_thing = size(thing);
+        foundPos = find(thing, magicChar);
+        if( foundPos == size_thing ) break;
+        string_res mcOccur = { thing, SHARE_EDITS, foundPos, foundPos+1 };
+        mcOccur = "";
+    }
+}
+
+#elif defined IMPL_STL
+void processThing( string & thing, char magicChar ) {
+    MAYBE(PRINT(thing));
+    size_t foundPos;
+    for (;;) {
+        size_t size_thing = thing.length();
+        foundPos = thing.find(magicChar);
+        if (foundPos == string::npos) break;
+        thing.erase(foundPos, 1);
+    }
+}
+
+#endif
+
+/*
+void runCorrectnessDemo(char** corpus, int corpusLen) {
+    STRING_T item = "asdf";    PRINT( processThing( item, '-' ) );
+    item = "as-df";            PRINT( processThing( item, '-' ) );
+    item = "-asdf-";           PRINT( processThing( item, '-' ) );
+    item = "-";                PRINT( processThing( item, '-' ) );
+    for ( int i = 0; i < corpusLen; i ++ ) {
+        item = corpus[i];      PRINT( processThing( item, '-' ) );
+    }
+
+    STRING_T supercorpus;
+    makeSuperCorpus( supercorpus, corpusLen, corpus, 30, ',');
+    PRINT( supercorpus );
+}
+*/
+
+int main( int argc, char ** argv ) {
+
+    STRING_SHARING_CONTROL
+
+    int corpuslen = 0;
+    char ** corpus = (char**) 0;
+
+    clock_t start, endTarget, end_actual;
+
+    const char * usage_args = "SCsize MagicChar ExecTimeSecs Corpus...";
+    const int static_arg_posns = 4;
+
+    int scSize = -1;
+    char magicChar = '\0';
+    int execTimeSecs = -1;
+
+    switch (min(argc, static_arg_posns)) {
+      case 4: execTimeSecs = atoi(argv[3]);
+      case 3: magicChar = argv[2][0];
+      case 2: scSize = atoi(argv[1]);
+    }
+
+    corpuslen = argc - static_arg_posns;
+    corpus = argv + static_arg_posns;
+
+    if (scSize < 1 || magicChar == '\0' || execTimeSecs < 1 || corpuslen < 1) {
+      printf("usage: %s %s\n", argv[0], usage_args);
+      printf("output:\nxxx,corpusItemCount,corpusMeanLenChars,invcationCountActual,execTimeActualSec\n");
+      exit(1);
+    }
+
+    double meanCorpusLen = meanLen(corpuslen, corpus);
+
+//    runCorrectnessDemo(corpus, corpuslen);
+
+
+    STRING_T supercorpus;
+    makeSuperCorpus( supercorpus, corpuslen, corpus, scSize, ',');
+
+    size_t sc_charlen = LEN(supercorpus);
+
+    start = clock();
+    endTarget = start + CLOCKS_PER_SEC * execTimeSecs;
+
+    volatile unsigned int t = 0;
+    size_t lastChunkEnd = -1;
+    for ( ; t % 10000 != 0 || clock() < endTarget ; t += 1 ) {
+            #if defined OP_PNO
+
+                lastChunkEnd++;
+                if ( lastChunkEnd >= sc_charlen ) {
+                    lastChunkEnd = 0;
+                }
+
+#if defined IMPL_CFA_HL
+                size_t thisChunkEnd = findFrom( supercorpus, lastChunkEnd, ',' );
+                //if (thisChunkEnd == string::npos) throw 1;
+                string chunkProcessed = supercorpus(lastChunkEnd, thisChunkEnd);
+                processThing(chunkProcessed, '-');
+#elif defined IMPL_CFA_LL
+                size_t thisChunkEnd = findFrom( supercorpus, lastChunkEnd, ',' );
+                //if (thisChunkEnd == string::npos) throw 1;
+                
+                string_res chunkProcessed = { supercorpus, COPY_VALUE, lastChunkEnd, thisChunkEnd };
+                processThing( chunkProcessed, '-' );
+#elif defined IMPL_STL
+                size_t thisChunkEnd = supercorpus.find( ',', lastChunkEnd );
+                if (thisChunkEnd == string::npos) throw 1;
+                string chunkProcessed = supercorpus.substr(lastChunkEnd, thisChunkEnd-lastChunkEnd);
+                processThing(chunkProcessed, '-');
+#else 
+#error Bad IMPL_
+#endif
+                MAYBE( PRINT(chunkProcessed) );
+
+                lastChunkEnd = thisChunkEnd;
+            #else
+                #error Bad OP_
+            #endif
+    }
+    end_actual = clock();
+    double elapsed = ((double) (end_actual - start)) / CLOCKS_PER_SEC;
+    printf("xxx,%d,%f,%d,%f\n", corpuslen, meanCorpusLen, t, elapsed);
+
+    // malloc_stats();
+
+    return 0;
+}
Index: tests/zombies/string-perf/prog-passbyX.cfa
===================================================================
--- tests/zombies/string-perf/prog-passbyX.cfa	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
+++ tests/zombies/string-perf/prog-passbyX.cfa	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
@@ -0,0 +1,210 @@
+#if defined IMPL_STL_NA_NA
+  #define IMPL_STL
+#endif
+
+#if defined IMPL_BUHR94_NA_NA
+  #define IMPL_BUHR94
+#endif
+
+#if defined IMPL_STL
+  #include <string>
+  #include <iostream>
+  #include <cstdio>
+  using namespace std;
+  #define IMPL_CXX
+
+#elif defined IMPL_CFA_HL_SHARE
+  #define IMPL_CFA_HL
+  #define IMPL_CFA
+
+#elif defined IMPL_CFA_LL_SHARE
+  #define IMPL_CFA_LL
+  #define IMPL_CFA
+
+#elif defined IMPL_CFA_HL_NOSHARE
+  #define IMPL_CFA_HL
+  #define CFA_NOSHARE
+  #define IMPL_CFA
+
+#elif defined IMPL_CFA_LL_NOSHARE
+  #define IMPL_CFA_LL
+  #define CFA_NOSHARE
+  #define IMPL_CFA
+
+#elif defined IMPL_BUHR94
+  #include <iostream>
+  #include <cstdio>
+  #include "/u0/mlbrooks/usys1/sm/string/StringSharing/src/string.h"
+  #define IMPL_CXX
+
+#else
+  #error Bad IMPL_
+#endif
+
+
+#if defined IMPL_CFA_HL
+  #include <string.hfa>
+#elif defined IMPL_CFA_LL
+  #include <string_res.hfa>
+  #include "not_string_res.hfa"
+#endif
+
+#if defined CFA_NOSHARE
+  #include <string_sharectx.hfa>
+  #define STRING_SHARING_CONTROL \
+    string_sharectx c = { NO_SHARING };
+#else 
+  #define STRING_SHARING_CONTROL
+#endif
+
+#if defined IMPL_CFA
+  #include <math.hfa>
+  extern "C" {
+    void malloc_stats( void );
+  }
+#elif defined IMPL_CXX
+  #include <algorithm>
+  using std::min;
+  #include <malloc.h>
+#endif
+
+#include <time.h>
+#include <stdlib.h> // atoi
+#include <string.h> // strlen, only during setup
+#include <limits.h> // LONG_MAX
+
+#if defined IMPL_STL || defined IMPL_BUHR94
+    #define PRINT(s) std::cout << s << std::endl
+#elif defined IMPL_CFA_HL || defined IMPL_CFA_LL
+    #define PRINT(s) sout | s;
+#else
+    #error Unhandled print case
+#endif
+
+#if defined IMPL_CFA_LL
+    #define STRING_T string_res
+    #define ASSIGN_CHAR(str, idx, val) assignAt(str, idx, val)
+#else
+    #define STRING_T string
+    #define ASSIGN_CHAR(str, idx, val) str[idx] = val
+#endif
+
+double meanLen(int N, char ** strings) {
+    int totalLen = 0;
+    for (int i = 0 ; i < N; i ++) {
+        totalLen += strlen(strings[i]);
+    }
+    return (double)totalLen / (double)N;
+}
+
+volatile int checkthis = 0;
+#define MAYBE( op ) if (checkthis) { op; }
+
+
+#if defined IMPL_CFA_LL
+void helper( string_res & q ) {
+#else
+  #error bad IMPL_
+#endif
+    #if defined OP_PB1X || defined OP_PB2X || defined OP_PB3X || defined OP_PB4X || defined OP_PB5X || defined OP_PB6X
+        ASSIGN_CHAR(q, 0, '@');
+    #else
+        MAYBE(ASSIGN_CHAR(q, 0, '@'));
+    #endif
+    MAYBE(PRINT(q));
+}
+
+void not_helper( not_string_res & q ) {
+    MAYBE( printf("%ld", q.junk[3]) );
+    MAYBE( q.junk[3] = 17 );
+}
+
+int main( int argc, char ** argv ) {
+
+    STRING_SHARING_CONTROL
+
+
+    const char * usage_args[] = {"(Ignored)    ExecTimeSecs   Corpus...",
+                                 "(Ignored) -w WorkAllocCount Corpus..."};
+
+    const int static_arg_posns = 3;
+    int used_arg_posns = static_arg_posns;
+
+    int execTimeSecs = -1;
+
+    long int iterationCountTarget = -1;
+
+    switch (min(argc, static_arg_posns)) {
+      case 3: if ( strcmp(argv[2], "-w") == 0 ) {
+                used_arg_posns ++;
+                execTimeSecs = 0;
+                iterationCountTarget = atoi(argv[3]);
+              } else {
+                execTimeSecs = atoi(argv[2]);
+                iterationCountTarget = LONG_MAX;
+              }
+    }
+
+    int corpuslen = argc - used_arg_posns;
+    char ** corpus = argv + used_arg_posns;
+
+    if ((execTimeSecs < 1 && iterationCountTarget < 1)  || corpuslen < 1) {
+      for (int u = 0; u < sizeof(usage_args) / sizeof(*usage_args); u++) {
+        printf("usage: %s %s\n", argv[0], usage_args[u]);
+      }
+      printf("output:\nxxx,corpusItemCount,corpusMeanLenChars,callDoneActualCount,execTimeActualSec\n");
+      exit(1);
+    }
+
+    double meanCorpusLen = meanLen(corpuslen, corpus);
+
+    clock_t start, end_target, end_actual;
+
+    STRING_T corpus_imported[corpuslen];
+
+    for (int i = 0; i < corpuslen; i++) {
+        corpus_imported[i] = corpus[i];
+        // if the callee ever modifies, then we will drive GCs, which will visit the entire corpus
+    }
+
+    start = clock();
+    if (execTimeSecs != 0) {
+      end_target = start + CLOCKS_PER_SEC * execTimeSecs;
+    } else {
+      end_target = start + CLOCKS_PER_SEC * 3600;
+    }
+
+    unsigned int t = 0;
+    for ( ; t < iterationCountTarget && ((t+1) % 10000 != 0 || clock() < end_target) ; t += 1 ) {
+            string_res & src = corpus_imported[t % corpuslen];
+            size_t srclen = size(src);
+            if (srclen < 2) {
+              printf("need string lengths >= 2");
+            }
+            #if defined OP_PB1 || defined OP_PB1X
+              helper( (string_res){ src, COPY_VALUE, 1, srclen-1 } ); 
+            #elif defined OP_PB2 || defined OP_PB2X
+              helper( (string_res){ src, SHARE_EDITS, 1, srclen-1 } );
+            #elif defined OP_PB3 || defined OP_PB3X
+              helper( (string_res){ src, COPY_VALUE, 0, srclen } );
+            #elif defined OP_PB4 || defined OP_PB4X
+              helper( (string_res){ src, SHARE_EDITS, 0, srclen } );
+            #elif defined OP_PB5 || defined OP_PB5X
+              helper( (string_res){ src, COPY_VALUE } );
+            #elif defined OP_PB6 || defined OP_PB6X
+              helper( src ); 
+            #elif defined OP_PB9
+              not_helper( (not_string_res){ src, SHARE_EDITS, 0, srclen } );
+            #else
+                #error Bad OP_
+            #endif
+    }
+    end_actual = clock();
+    unsigned int callsDone = t;
+    double elapsed = ((double) (end_actual - start)) / CLOCKS_PER_SEC;
+    printf("xxx,%d,%f,%d,%f\n", corpuslen, meanCorpusLen, t, elapsed);
+
+    // malloc_stats();
+
+    return 0;
+}
Index: tests/zombies/string-perf/prog-passbyval.cfa
===================================================================
--- tests/zombies/string-perf/prog-passbyval.cfa	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
+++ tests/zombies/string-perf/prog-passbyval.cfa	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
@@ -0,0 +1,164 @@
+#if defined IMPL_STL_NA_NA
+  #define IMPL_STL
+#endif
+
+#if defined IMPL_BUHR94_NA_NA
+  #define IMPL_BUHR94
+#endif
+
+#if defined IMPL_STL
+  #include <string>
+  #include <iostream>
+  #include <cstdio>
+  using namespace std;
+  #define IMPL_CXX
+
+#elif defined IMPL_CFA_HL_SHARE
+  #define IMPL_CFA_HL
+  #define IMPL_CFA
+
+#elif defined IMPL_CFA_LL_SHARE
+  #define IMPL_CFA_LL
+  #define IMPL_CFA
+
+#elif defined IMPL_CFA_HL_NOSHARE
+  #define IMPL_CFA_HL
+  #define CFA_NOSHARE
+  #define IMPL_CFA
+
+#elif defined IMPL_CFA_LL_NOSHARE
+  #define IMPL_CFA_LL
+  #define CFA_NOSHARE
+  #define IMPL_CFA
+
+#elif defined IMPL_BUHR94
+  #include <iostream>
+  #include <cstdio>
+  #include "/u0/mlbrooks/usys1/sm/string/StringSharing/src/string.h"
+  #define IMPL_CXX
+
+#else
+  #error Bad IMPL_
+#endif
+
+
+#if defined IMPL_CFA_HL
+  #include <string.hfa>
+#elif defined IMPL_CFA_LL
+  #include <string_res.hfa>
+#endif
+
+#if defined CFA_NOSHARE
+  #include <string_sharectx.hfa>
+  #define STRING_SHARING_CONTROL \
+    string_sharectx c = { NO_SHARING };
+#else 
+  #define STRING_SHARING_CONTROL
+#endif
+
+#if defined IMPL_CFA
+  #include <math.hfa>
+  extern "C" {
+    void malloc_stats( void );
+  }
+#elif defined IMPL_CXX
+  #include <algorithm>
+  using std::min;
+  #include <malloc.h>
+#endif
+
+#include <time.h>
+#include <stdlib.h> // atoi
+#include <string.h> // strlen, only during setup
+
+#if defined IMPL_STL || defined IMPL_BUHR94
+    #define PRINT(s) std::cout << s << std::endl
+#elif defined IMPL_CFA_HL || defined IMPL_CFA_LL
+    #define PRINT(s) sout | s;
+#else
+    #error Unhandled print case
+#endif
+
+#if defined IMPL_CFA_LL
+    #define STRING_T string_res
+    #define ASSIGN_CHAR(str, idx, val) assignAt(str, idx, val)
+#else
+    #define STRING_T string
+    #define ASSIGN_CHAR(str, idx, val) str[idx] = val
+#endif
+
+double meanLen(int N, char ** strings) {
+    int totalLen = 0;
+    for (int i = 0 ; i < N; i ++) {
+        totalLen += strlen(strings[i]);
+    }
+    return (double)totalLen / (double)N;
+}
+
+volatile int checkthis = 0;
+#define MAYBE( op ) if (checkthis) { op; }
+
+
+#if defined IMPL_CFA_LL
+void helper( string_res & qref ) {
+    string_res q = { qref, COPY_VALUE };
+#else
+void helper( string q ) {
+#endif
+    MAYBE(ASSIGN_CHAR(q, 0, '@'));
+    MAYBE(PRINT(q));
+}
+
+int main( int argc, char ** argv ) {
+
+    STRING_SHARING_CONTROL
+
+
+    const char * usage_args = "(Ignored) ExecTimeSecs Corpus...";
+    const int static_arg_posns = 3;
+
+    int execTimeSecs = -1;
+
+    switch (min(argc, static_arg_posns)) {
+      case 3: execTimeSecs = atoi(argv[2]);
+    }
+
+    int corpuslen = argc - static_arg_posns;
+    char ** corpus = argv + static_arg_posns;
+
+    if (execTimeSecs < 1 || corpuslen < 1) {
+      printf("usage: %s %s\n", argv[0], usage_args);
+      printf("output:\nxxx,corpusItemCount,corpusMeanLenChars,callDoneActualCount,execTimeActualSec\n");
+      exit(1);
+    }
+
+    double meanCorpusLen = meanLen(corpuslen, corpus);
+
+    clock_t start, end_target, end_actual;
+
+    STRING_T corpus_imported[corpuslen];
+
+    for (int i = 0; i < corpuslen; i++) {
+        corpus_imported[i] = corpus[i];
+        // if the callee ever modifies, then we will drive GCs, which will visit the entire corpus
+    }
+
+    start = clock();
+    end_target = start + CLOCKS_PER_SEC * execTimeSecs;
+    unsigned int t = 0;
+    for ( ; t % 10000 != 0 || clock() < end_target ; t += 1 ) {
+            #if defined OP_PBV
+            helper( corpus_imported[t % corpuslen] );
+            #else
+                #error Bad OP_
+            #endif
+    }
+    end_actual = clock();
+    unsigned int callsDone = t;
+    double elapsed = ((double) (end_actual - start)) / CLOCKS_PER_SEC;
+    printf("xxx,%d,%f,%d,%f\n", corpuslen, meanCorpusLen, t, elapsed);
+
+    // malloc_stats();
+
+    return 0;
+}
Index: tests/zombies/string-perf/prog.cfa
===================================================================
--- tests/zombies/string-perf/prog.cfa	(revision fefd77adcd0a40d9ea468278e2369a92cfd9e773)
+++ tests/zombies/string-perf/prog.cfa	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
@@ -1,2 +1,9 @@
+#if defined IMPL_STL_NA_NA
+  #define IMPL_STL
+#endif
+
+#if defined IMPL_BUHR94_NA_NA
+  #define IMPL_BUHR94
+#endif
 
 #if defined IMPL_STL
@@ -52,7 +59,11 @@
 #if defined IMPL_CFA
   #include <math.hfa>
+  extern "C" {
+    void malloc_stats( void );
+  }
 #elif defined IMPL_CXX
   #include <algorithm>
   using std::min;
+  #include <malloc.h>
 #endif
 
@@ -114,9 +125,9 @@
     #if defined IMPL_CFA_LL
       #define DECLS \
-        string_res initval = "starter"; \
-        string_res accum = { initval, COPY_VALUE };
+        const char* initval = "starter"; \
+        string_res accum = initval;
     #else
       #define DECLS \
-        string initval = "starter"; \
+        const char* initval = "starter"; \
         string accum = initval;
     #endif
@@ -149,4 +160,6 @@
               #elif defined OP_PEQ
                  accum += toAppend;
+              #else
+                 #error Bad OP_
               #endif
             }
@@ -157,4 +170,6 @@
     printf("%d,%d,%f,%d,%f\n", concatsPerReset, corpuslen, meanCorpusLen, concatsDone, elapsed);
 
+    // malloc_stats();
+
     return 0;
 }
Index: tests/zombies/string-perf/qanal.py
===================================================================
--- tests/zombies/string-perf/qanal.py	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
+++ tests/zombies/string-perf/qanal.py	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
@@ -0,0 +1,34 @@
+#! /usr/bin/python3
+
+import glob
+import os
+import re
+
+print("corpuslen", "expansion", "elapsed_whole_test", "elapsed_last_alloc", "seclast_req_mem", sep=",")
+
+thepath = "preload_dump--qrun1*"
+
+for mypath in glob.glob(thepath):
+    pathparsed=re.match(r'preload_dump--qrun1--corpus-1-(\d+)-1.txt--expansion-(0\.\d+).txt', mypath)
+    corpuslen=pathparsed.group(1)
+    expansion=pathparsed.group(2)
+
+    with open(mypath) as file:
+        lines = file.readlines()
+
+        nAllocs = len(lines)
+
+        epoch_timestamp_bp = lines[0].find("epoch_timestamp(ms)")
+        epoch_timestamp_ep = epoch_timestamp_bp + len("epoch_timestamp(ms)")
+        current_req_mem_bp = lines[0].find("current_req_mem(B)")
+        current_req_mem_ep = current_req_mem_bp + len("current_req_mem(B)")
+        
+        first_timestamp   = lines[        1][epoch_timestamp_bp:epoch_timestamp_ep].strip()
+        seclast_req_mem   = lines[nAllocs-2][current_req_mem_bp:current_req_mem_ep].strip()
+        seclast_timestamp = lines[nAllocs-2][epoch_timestamp_bp:epoch_timestamp_ep].strip()
+        last_timestamp    = lines[nAllocs-1][epoch_timestamp_bp:epoch_timestamp_ep].strip()
+
+        elapsed_whole_test = int(last_timestamp) - int(first_timestamp)
+        elapsed_last_alloc = int(last_timestamp) - int(seclast_timestamp)
+
+        print(corpuslen, expansion, elapsed_whole_test, elapsed_last_alloc, seclast_req_mem, sep=",")
Index: tests/zombies/string-perf/qrun.sh
===================================================================
--- tests/zombies/string-perf/qrun.sh	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
+++ tests/zombies/string-perf/qrun.sh	(revision 33b7d490d37c1f84e96a85cdf1c8baad2b08aeb8)
@@ -0,0 +1,10 @@
+
+set SIZES='20 50 100 200 500'
+set CFA_EXPANSIONS='0.02 0.05 0.1 0.2 0.5 0.9'
+
+for size in $SIZES ; do
+    echo a
+    for expansion in $CFA_EXPANSIONS ; do
+       echo LD_PRELOAD=~/plg2/mubeen-stat-shim/malloc/mallocWrappers.so ~/plg2/cfa2/cfa-cc/tests/zombies/string-perf/perfexp-cfa-pall-ll-share-na 1000 1.006 $expansion 1 caaaat ~/plg2/cfa2/cfa-cc/tests/zombies/string-perf/corpus-1-$size-1.txt
+    done ;
+done
