This is an automated email from the ASF dual-hosted git repository.
shuyangw pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new a015dc3 test: check leak for client abort (#4613)
a015dc3 is described below
commit a015dc378f64d15c205377ae52759be46de346fb
Author: 罗泽轩 <[email protected]>
AuthorDate: Tue Jul 20 03:32:56 2021 +0800
test: check leak for client abort (#4613)
Signed-off-by: spacewander <[email protected]>
---
.github/workflows/fuzzing-ci.yaml | 4 +++
t/fuzzing/client_abort.py | 60 +++++++++++++++++++++++++++++++++++++++
t/fuzzing/public.py | 52 +++++++++++++++++++++++++++++++++
t/fuzzing/upstream/nginx.conf | 6 ++++
4 files changed, 122 insertions(+)
diff --git a/.github/workflows/fuzzing-ci.yaml
b/.github/workflows/fuzzing-ci.yaml
index 7b4bf53..43de19d 100644
--- a/.github/workflows/fuzzing-ci.yaml
+++ b/.github/workflows/fuzzing-ci.yaml
@@ -78,3 +78,7 @@ jobs:
- name: run vars route test
run: |
python $PWD/t/fuzzing/vars_route_test.py
+
+ - name: run client abort test
+ run: |
+ python $PWD/t/fuzzing/client_abort.py
diff --git a/t/fuzzing/client_abort.py b/t/fuzzing/client_abort.py
new file mode 100755
index 0000000..707297e
--- /dev/null
+++ b/t/fuzzing/client_abort.py
@@ -0,0 +1,60 @@
+#! /usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+
+import http.client
+import subprocess
+import time
+import threading
+from public import check_leak, run_test
+
+def create_route():
+ command = '''curl -i http://127.0.0.1:9080/apisix/admin/routes/1 -H
'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "uri": "/client_abort",
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:6666": 1
+ },
+ "type": "roundrobin"
+ }
+}'
+ '''
+ subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
shell=True)
+
+def req():
+ conn = http.client.HTTPConnection("127.0.0.1", port=9080)
+ conn.request("GET", "/client_abort?seconds=0.01")
+ time.sleep(0.001)
+ conn.close()
+
+def run_in_thread():
+ for i in range(50):
+ req()
+
+@check_leak
+def run():
+ th = [threading.Thread(target=run_in_thread) for i in range(10)]
+ for t in th:
+ t.start()
+ for t in th:
+ t.join()
+
+
+if __name__ == "__main__":
+ run_test(create_route,run)
diff --git a/t/fuzzing/public.py b/t/fuzzing/public.py
index de664ef..4d67baf 100644
--- a/t/fuzzing/public.py
+++ b/t/fuzzing/public.py
@@ -15,8 +15,10 @@
# limitations under the License.
#
+import http.client
import subprocess
import os
+from functools import wraps
from pathlib import Path
import psutil
from boofuzz import FuzzLoggerText, Session, TCPSocketConnection, Target
@@ -63,6 +65,56 @@ def initfuzz():
)
return session
+def sum_memory():
+ pmap = {}
+ for p in check_process():
+ proc = psutil.Process(p)
+ pmap[proc] = proc.memory_full_info()
+ return sum(m.rss for m in pmap.values())
+
+def get_linear_regression_sloped(samples):
+ n = len(samples)
+ avg_x = (n + 1) / 2
+ avg_y = sum(samples) / n
+ avg_xy = sum([(i + 1) * v for i, v in enumerate(samples)]) / n
+ avg_x2 = sum([i * i for i in range(1, n + 1)]) / n
+ denom = avg_x2 - avg_x * avg_x
+ if denom == 0:
+ return None
+ return (avg_xy - avg_x * avg_y) / denom
+
+def gc():
+ conn = http.client.HTTPConnection("127.0.0.1", port=9090)
+ conn.request("POST", "/v1/gc")
+ conn.close()
+
+def check_leak(f):
+ @wraps(f)
+ def wrapper(*args, **kwds):
+ leak_count = 100
+
+ samples = []
+ for i in range(leak_count):
+ f(*args, **kwds)
+ gc()
+ samples.append(sum_memory())
+ count = 0
+ for i in range(1, leak_count):
+ if samples[i - 1] < samples[i]:
+ count += 1
+ print(samples)
+ sloped = get_linear_regression_sloped(samples)
+ print(sloped)
+ print(count / leak_count)
+
+ if os.environ.get("CI"): # CI is not stable
+ return
+
+ if sloped > 1000 and (count / leak_count) > 0.1:
+ raise AssertionError("memory leak")
+
+ return wrapper
+
def run_test(create_route, run):
# before test
create_route()
diff --git a/t/fuzzing/upstream/nginx.conf b/t/fuzzing/upstream/nginx.conf
index 59c9d3f..3c64053 100644
--- a/t/fuzzing/upstream/nginx.conf
+++ b/t/fuzzing/upstream/nginx.conf
@@ -47,6 +47,12 @@ http {
ngx.say("cur time: ", ngx.time())
}
}
+
+ location /client_abort {
+ content_by_lua_block {
+ ngx.sleep(tonumber(ngx.var.arg_seconds or 1))
+ }
+ }
}
}