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))
+            }
+        }
     }
 
 }

Reply via email to