Mercurial > stress-tester
changeset 1237:e8d302936240
flow - switching to deterministic algo
author | Devel 1 |
---|---|
date | Tue, 30 Jun 2020 10:56:04 +0200 |
parents | d526a06722f7 |
children | 457d050a95f6 |
files | stress-tester/src/main/resources/flow_analyzer.py |
diffstat | 1 files changed, 155 insertions(+), 131 deletions(-) [+] |
line wrap: on
line diff
--- a/stress-tester/src/main/resources/flow_analyzer.py Tue Jun 30 09:04:20 2020 +0200 +++ b/stress-tester/src/main/resources/flow_analyzer.py Tue Jun 30 10:56:04 2020 +0200 @@ -93,14 +93,24 @@ lines = self.extract_lines(disallowedStrings=disallowedStrings, debug=debug) rows = [] - for line in lines: - line = json.loads(line) + if debug: + self.jsonReject = [] + self.jsonReject1 = [] + self.jsonAccepted = [] + for rline in lines: + line = json.loads(rline) try: line["httpreqheaders"] = line["httpreqheaders"][re.search("SESSION=", line["httpreqheaders"]).end():] - line["httpreqheaders"] = line["httpreqheaders"][:re.search("; ", line["httpreqheaders"]).start()] + line["httpreqheaders"] = line["httpreqheaders"][:re.search(";|\r\n", line["httpreqheaders"]).start()] + if debug: + self.jsonAccepted.append(rline) except: if debug: - print(line) + self.jsonReject.append(rline) + continue + if line["_type"] !="session.http.request": + if debug: + self.jsonReject1.append(rline) continue rows.append([ line[columnMap.get("id", "id")], @@ -114,7 +124,7 @@ self.data = df = pd.DataFrame(rows, columns=["id", "datetime", "requestMethod", "url", "protocol", "status", "agent"]) - # Linijskan ap otrzeby szybszych testów do skasowania + # Linijka na potrzeby szybszych testów do skasowania # self.data = self.data.iloc[0:10000] # Zmiana tekstowego formatu daty na datę pandasową, żeby można bylo skutecznie sortować @@ -147,15 +157,10 @@ def prep_data(self, cleanRows = True, cleanRules = None, dropMissing = True, debug=False): if cleanRules is None: self.cleanRules = [ - # Zasoby doatkowe typu css/js ".css", ".js", - # Elementy graficzne ".jpg", ".png", ".svg", ".gif", ".ico", - # Czcionki ".ttf", ".woff", ".woff2", ".eot", - # Dokumenty ".pdf", ".doc", ".docx", ".ppt", ".pptx", ".txt", - # Inne elementy jak np strony sprawdzające captch "captcha" ] else: @@ -407,66 +412,8 @@ self.allSequences = allSequences - def analyze_markov(self, historyLength = 1, targetLength = 5, shareLimit = 0.0001): - """ - Funkcja która wykona analizę sekwencji. Funkcja zakłada, że id sekwencji już zostały przygotowane: - historyLength: dla jak wielu elementów bierzemy historię by przewidywać następny element. - """ - - # transitions to słownik słowników mówiący ile razy przechodzimy zjednej historii do kolejnego zdarzenia - transitions = {} - # To ile będzie kluczy w pierwszym słowniku zalezy od głębokosci historii i liczby unikatowych kombinacji id - # To ile będzie kluczy w drugich słownikach zalezy od liczby unikatowych id - - totalNextSum = 0 - for seq in self.allSequences: - if len(seq) >= historyLength+1: - # Początek sekwencji to start - for i in range(historyLength, len(seq)): - keyName = "|".join([str(x) for x in seq[i-historyLength:i]]) - # Znajdujemy słownik z przejściami dla danej historii. - val = transitions.get(keyName, {}) - # Aktualizujemy słownik dodajć 1 - val[seq[i]] = val.get(seq[i], 0)+1 - totalNextSum +=1 - # Aktualizujemy słownik w głównym słowniku - transitions[keyName] = val - # Skoro już mamy wszystkie zdarzenia to wiemy ile razy która sekwencja będzie startowa - # w tradycyjnym podejściu markowa jesteśmy pewni gdzie jest początek sekwencji - # tutaj nie możemy być pewni więc zakładamy że może być w dowolnym miejscu - - # na początku będzie tyle "starterów" sekwencji ile mamy takich fragmentów w historii - sequences = [] - for key, value in transitions.items(): - # key to stringowe id sekwencji - # value to słownik z przejściami - # value.values() zawiera liczebności - # dana sekwencja key wystąpiła tyle razy co suma wartości liczników w wartościach - sequences.append((key.split("|"), sum(value.values()))) - - for k in range(targetLength-historyLength): - newSequences = [] - # Pogłębiamy sekwencje probabilistycznie - for seq, number in sequences: - # - keyName = "|".join([str(x) for x in seq[-historyLength:]]) - # Znajdujemy słownik z przejściami dla danej historii. - val = transitions.get(keyName, {}) - total = sum(val.values()) - - for nextEl, numberNext in val.items(): - if (number*numberNext/total)/totalNextSum > shareLimit: - newSequences.append((seq+[str(nextEl)], number*numberNext/total)) - sequences = newSequences - - # Normalizujemy i sortujemy - self.markov_not_norm = sorted([xy for xy in sequences], reverse=True, key=lambda x: x[1]) - self.markov = [(x, y / totalNextSum) for x, y in self.markov_not_norm] - self.markovMapped = [([self.idsMapRev[int(z)] for z in x], y / totalNextSum) for x, y in self.markov_not_norm] - return self.markovMapped - - def analyze_markov_fold(self, historyLength=1, targetLength=5, shareLimit=0.0001, - foldLoops=False, foldMaxElements=1, debug=False): + def analyze_markov(self, historyLength = 1, targetLength = 5, shareLimit = 0.0001, + foldLoops=False, foldMaxElements=1, debug=False): """ Funkcja która wykona analizę sekwencji. Funkcja zakłada, że id sekwencji już zostały przygotowane: historyLength: dla jak wielu elementów bierzemy historię by przewidywać następny element. @@ -482,7 +429,7 @@ if len(seq) >= historyLength + 1: # Początek sekwencji to start for i in range(historyLength, len(seq)): - keyName = "|".join([str(x) for x in seq[i - historyLength:i]]) + keyName = "|".join(str(x) for x in seq[i - historyLength:i]) # Znajdujemy słownik z przejściami dla danej historii. val = transitions.get(keyName, {}) # Aktualizujemy słownik dodajć 1 @@ -511,7 +458,7 @@ # Pogłębiamy sekwencje probabilistycznie for seq, number in sequences: # - keyName = "|".join([str(x) for x in seq[-historyLength:]]) + keyName = "|".join(str(x) for x in seq[-historyLength:]) # Znajdujemy słownik z przejściami dla danej historii. val = transitions.get(keyName, {}) total = sum(val.values()) @@ -530,19 +477,15 @@ for foldWidth in range(1, foldMaxElements + 1): # Zataczamy pętlę jeżeli mamy {Z}XYX, gdzie: # Z - cokolwiek wcześniej - # X ma szerokość historyLength + # X ma szerokość 1 # zaś Y ma szerokość określoną w foldWidth maksymalnie foldMaxElements - if len(seq) >= (2 * historyLength + foldWidth): - # print(seq, - # seq[-(2*historyLength+foldWidth):-(historyLength+foldWidth)], - # seq[-historyLength:], - # seq[-(2*historyLength+foldWidth):-(historyLength+foldWidth)] == seq[-historyLength:]) - if seq[-(2 * historyLength + foldWidth):-(historyLength + foldWidth)] == seq[-historyLength:]: + if len(seq) >= (2 + foldWidth): + if seq[-(2 + foldWidth):-(1 + foldWidth)] == seq[-1:]: # Jest pętle, usuwamy sekwencję (nie dodajemy) # Sekwencję przenosimy do listę pętli # Pierwszy element to ZXY - potrzebny do zaraportowania # Drugi to ZX - potrzebny do modyfikacji liczebności - newLoops.append((seq.copy(), seq[:-(historyLength + foldWidth)], number)) + newLoops.append((seq.copy(), seq[:-(1 + foldWidth)], number)) seqIsLoop = True # breakujemy fora bo jak jest lupem n to nei ma sensu szukać n+1 break @@ -586,6 +529,27 @@ if debug: print("newLoopsCounts", newLoopsCounts[0:10]) + # Jeżeli okazuje się że są pętle, których nie ma na co przełożyć to musimy je dać spowrotem na liste sekwencji + # Chyba się nie da tego zrobić prościej poniewaz musimy ro zrobić nawet jeżeli mamy dwie pętle + # Ale i tak nie ma tego na co przerzucić + # Przykładowo: + idsToPop = set() + for i, (loop, loopID, loopNumber, subSeqLoopCounter) in enumerate(newLoopsCounts): + if subSeqLoopCounter == 0: + idsToPop.add(i) + newSequences.append((loop, loopNumber)) + # To jest pętla której nie ma na co przerzucić + # Dodajmy ją więc do sekwencji + # Jeżeli coś trafiło na liste sekwencji to musi zniknąć z listy loopsów + newLoopsCounts = [x for i, x in enumerate(newLoopsCounts) if i not in idsToPop] + + # Może się okazać, że znów nie mamy żadnych loopsów. + if len(newLoops)==0: + # Jeżeli to spełnione to ekwiwalent zakończ iteracji fora z Duża pętla + # Jeżeli nie znaleźlismy żadnej pętli to nie ma co mielić + # Przechodzimy do kolejnej iteracji + sequences = newSequences.copy() + continue for i in seqId2mod: seq, number = newSequences[i] @@ -609,55 +573,102 @@ sequences = newSequences # Normalizujemy i sortujemy - self.markov_not_norm = sorted([xy for xy in sequences], reverse=True, key=lambda x: x[1]) - self.markov = [(x, y / totalNextSum) for x, y in self.markov_not_norm] - self.markovMapped = [([self.idsMapRev[int(z)] for z in x], y / totalNextSum) for x, y in self.markov_not_norm] - self.markovLoopsMapped = [([self.idsMapRev[int(x)] for x in xx], [self.idsMapRev[int(y)] for y in yy], z, v) for xx, yy, z, v in self.markovLoops] + self.markovNotNorm = sorted(sequences, reverse=True, key=lambda x: x[1]) + self.markov = [(x, y / totalNextSum) for x, y in self.markovNotNorm] + self.markovMapped = [(self.seq_id_to_path(x), y / totalNextSum) for x, y in self.markovNotNorm] + #self.markovLoopsMapped = [(self.seq_id_to_path(x), self.seq_id_to_path(y), z, v) for x, y, z, v in self.markovLoops] return self.markovMapped + def analyze(self, targetLength = 5, foldLoops=False, foldMaxElements=1, debug=False): + def foldSeq(seq, foldMaxElements=1, seqLoops = []): + for k in range(len(seq)): + for width in range(1, foldMaxElements+1): -def find_sub_list(haystack, needle): - results = [] - sll = len(needle) - for ind in (i for i, e in enumerate(haystack) if e == needle[0]): - if haystack[ind: ind + sll] == needle: - results.append(ind) - return results + if len(seq) < k+2+width: + continue + # Sprawdzamy czy znaleźliśmy pętlę + if seq[k:k+1] == seq[k+width+1:k+width+2]: + # Jeżeli tak to musimy skasować pętlę: + tmp = seq[0:k] + seq[k+width+1:] + # Dodać tę pętlę do listy pętli tej sekwencji + seqLoops.append((seq[:k+1], seq[k+1:k+width+2])) + # Skoro znaleźlismy pętle to nie chcemy pracować już na oryginalnej sekwencji + # Ale na takiej z wyciętą pętlą + # Zwróćmy więc wynik pracy na wycętej sekwencji + # Pamiętając o tym co już wycieliśmy + return foldSeq(tmp, foldMaxElements, seqLoops) + # Jeżeli przeszliśmy przez całego for + # To w sekwencji nie znaleźlismy pętli + # Możemy zwrócić sekwencje i wcześniej znalezione + return seq, seqLoops -def replace_all_sublists(haystack, needle, replacement, locs): - delta = len(replacement) - len(needle) - off = 0 - - for loc in locs: - bgn = loc + off - del haystack[bgn:bgn + len(needle)] - for i, r in enumerate(replacement): - haystack.insert(bgn + i, replacement[i]) - off += delta - + allSeqFolded = [] + allLoops = [] + if foldLoops: + for seq in self.allSequences: + # Tutaj z jakiegoś powodu musiałem podawać wartość dla seqLoops + folded, loops = foldSeq(seq, foldMaxElements=foldMaxElements, seqLoops=[]) + allSeqFolded.append(folded) + allLoops.append(loops) + else: + allSeqFolded = self.allSequences + self.allLoops = allLoops.copy() + self.allSeqFolded = allSeqFolded.copy() + -def replace_sublist(haystack, needle, replacement, bgn): - del haystack[bgn:bgn + len(needle)] - for i, r in enumerate(replacement): - haystack.insert(bgn + i, replacement[i]) - + seqCounts = dict() + self.seqMap = dict() + for i, seq in enumerate(allSeqFolded): + for k in range(max(1, len(seq)-targetLength+1)): + tmp = seq[k:k+targetLength] + key = "|".join(str(x) for x in tmp) + # Zliczamy wszystkie subsekwencje + seqCounts[key] = seqCounts.get(key, 0)+1 + # Na wszelki wypadek zapiszmy z której oryginalnej sekwencji są tesubsekwencje + # Będziemy mogli łatwo znaleźć "źródło" + tmpl = self.seqMap.get(key, []) + tmpl.append(i) + self.seqMap[key] = tmpl.copy() + tmp = sorted(list(Counter(seqCounts).items()), key=lambda x: x[1], reverse=True) + totalN = sum(y for x,y in tmp) + + self.analyzedNotNorm = [(x.split("|"), y) for x,y in tmp] + self.analyzed = [(x, y / totalN) for x, y in self.analyzedNotNorm] + self.analyzedMapped = [(self.seq_id_to_path(x), y / totalN) for x, y in self.analyzedNotNorm] + return self.analyzedMapped -def expand(seqs, folds): - # print('{\n"seqs" :', json.dumps(seqs), ",") - # print('"folds": ', json.dumps(folds), "\n}\n") - results = [] - for seq in seqs: - results.append(seq) - # print("processing ", seq) - for repl, needle, x, y in folds: - locs = find_sub_list(seq, needle) - if len(locs) > 0: - newseq = seq.copy() - replace_sublist(newseq, needle, repl, locs[0]) - results.append(newseq) - # print(seq, needle, locs, repl, ' => ', newseq) - return results + def get_unfolded(self, needle): + unfolded = set() + needleL= len(needle) + for k in self.seqMap["|".join(str(x) for x in needle)]: + seqFolded = self.allSeqFolded[k] + seq = self.allSequences[k] + loops = self.allLoops[k] + + positions = [] + for pos in range(len(seqFolded) - needleL + 1): + if seqFolded[pos:pos+needleL] == needle: + positions.append((pos, pos + needleL)) + newPositions = positions.copy() + + for loop, appendix in loops: + pos = len(loop) + width = len(appendix) + for i, (start, finish) in enumerate(positions): + nstart, nfisnish = newPositions[i] + if start > pos: + nstart += width + if finish >= pos: + nfisnish += width + newPositions[i] = (nstart, nfisnish) + for start, finish in newPositions: + unfolded.add("|".join(str(x) for x in seq[start:finish])) + unfolded = [x.split("|") for x in unfolded] + return unfolded + + def seq_id_to_path(self, seq): + return [self.idsMapRev[int(z)] for z in seq] if __name__ == "__main__": @@ -693,16 +704,29 @@ for length in range(args.lmin, args.lmax + 1): if args.fold: suffix = 'f' - res = analyzer.analyze_markov_fold(targetLength=length, foldLoops=True) + res = analyzer.analyze(targetLength=length, foldLoops=True) seqs_prob = [(s, prob) for s, prob in res if prob >= args.pmin] seqs = [s for s, prob in res if prob >= args.pmin] - loops = analyzer.markovLoopsMapped - seqs_exp = expand(seqs, loops) + seqs_id = [(s, prob) for s, prob in analyzer.analyzed if prob >= args.pmin] + + unfolded = [] + num_unfolded = 0 + for (s, prob) in seqs_id: + seq_mapped = analyzer.seq_id_to_path(s) + si = [int(x) for x in s] + unfolded_ids = analyzer.get_unfolded(si) + unfolded_mapped = [analyzer.seq_id_to_path(x) for x in unfolded_ids] + unfolded.append({ + 'sequence': seq_mapped, + 'prob': prob, + 'unfolded': unfolded_mapped + }) + num_unfolded += len(unfolded_ids) + stats = { 'seqCount': len(res), 'seqCountP': len(seqs), - 'loopCount': len(loops), - 'expandedCount': len(seqs_exp) + 'unfoldedCount': num_unfolded } seqs = seqs[:args.maxSeq] seqs_prob = seqs_prob[:args.maxSeq] @@ -711,7 +735,7 @@ res = analyzer.analyze_markov(targetLength=length) seqs_prob = [(s, prob) for s, prob in res if prob >= args.pmin] seqs = [s for s, prob in res if prob >= args.pmin] - loops = None + unfolded = None stats = { 'seqCount': len(res), 'seqCountP': len(seqs) @@ -729,6 +753,6 @@ with open("seq_L{}{}_stats.json".format(length, suffix), "w") as f: json.dump(stats, f) # input to next stage - if loops: - with open("seq_L{}{}_loops.json".format(length, suffix), "w") as f: - json.dump(loops, f) + if unfolded: + with open("seq_L{}{}_unfolded.json".format(length, suffix), "w") as f: + json.dump(unfolded, f)