Repository: scoutapp/scout_apm_python

Issue:
Add ARM support
We've added **linux** ARM support (aarch64) for the core agent binary in version 1.3.1. We need to add ARM architecture detection on linux for the Python agent.

On AWS graviton instance:

```
ubuntu@ip-172-31-58-208:~$ uname -a
Linux ip-172-31-58-208 5.4.0-1045-aws #47-Ubuntu SMP Tue Apr 13 07:04:23 UTC 2021 aarch64 aarch64 aarch64 GNU/Linux
```

Note: I didn't try 3.9
Python 3.8
```
ubuntu@ip-172-31-58-208:~$ python3 --version
Python 3.8.10
ubuntu@ip-172-31-58-208:~$ python3
Python 3.8.10 (default, Jun  2 2021, 10:49:15)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import platform;
>>> platform.machine()
'aarch64'
>>>
```

Python 2.7:
```
ubuntu@ip-172-31-58-208:~$ python --version
Python 2.7.18

ubuntu@ip-172-31-58-208:~$ python
Python 2.7.18 (default, Mar  8 2021, 13:02:45)
[GCC 9.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import platform;
>>> platform.machine()
'aarch64'
>>>
```

The core agent 1.3.1 builds available:
```
core-agent-v1.3.1-aarch64-unknown-linux-gnu
core-agent-v1.3.1-aarch64-unknown-linux-musl
core-agent-v1.3.1-i686-unknown-linux-gnu
core-agent-v1.3.1-i686-unknown-linux-musl
core-agent-v1.3.1-x86_64-apple-darwin
core-agent-v1.3.1-x86_64-unknown-linux-gnu
core-agent-v1.3.1-x86_64-unknown-linux-musl

```


Discussion:
@dlanderson Sorry, I'm a bit confused on the details here. What's the functional change you're looking for?
I think this needs:
 *  `aarch64` added as an option at https://github.com/scoutapp/scout_apm_python/blob/master/src/scout_apm/core/platform_detection.py#L21 and any associated tests.
  * Bump core-agent version default to 1.3.1
  * We don't build for `darwin` ARM (M1 Machines), so we should still use `core-agent-v1.3.1-x86_64-apple-darwin` even if we detect something other than x86_64 on macs (Rosetta will run x86 binaries on M1 macs just fine). From brief searching there are different archs returned for M1 on certain python versions.
  * Do any versions of python return anything other than `aarch64`?
Thank you for the clarifications.

> Do any versions of python return anything other than aarch64?

I imagine this depends on the machine that's being used. From what I can tell, python will try to make use of linux's uname command. 


Can you share some of those searches with me that said different architectures are returned when using M1?

Using Amazons ARM-64 image, I tested this with py2.7, 3.5-3.10. All returned `aarch64`.

Resolving patch:
diff --git a/src/scout_apm/core/config.py b/src/scout_apm/core/config.py
--- a/src/scout_apm/core/config.py
+++ b/src/scout_apm/core/config.py
@@ -220,7 +220,7 @@ def __init__(self):
             "core_agent_log_level": "info",
             "core_agent_permissions": 700,
             "core_agent_socket_path": "tcp://127.0.0.1:6590",
-            "core_agent_version": "v1.3.0",  # can be an exact tag name, or 'latest'
+            "core_agent_version": "v1.3.1",  # can be an exact tag name, or 'latest'
             "disabled_instruments": [],
             "download_url": "https://s3-us-west-1.amazonaws.com/scout-public-downloads/apm_core_agent/release",  # noqa: B950
             "errors_batch_size": 5,
diff --git a/src/scout_apm/core/platform_detection.py b/src/scout_apm/core/platform_detection.py
--- a/src/scout_apm/core/platform_detection.py
+++ b/src/scout_apm/core/platform_detection.py
@@ -8,14 +8,28 @@ def is_valid_triple(triple):
     values = triple.split("-", 1)
     return (
         len(values) == 2
-        and values[0] in ("i686", "x86_64", "unknown")
+        and values[0] in ("i686", "x86_64", "aarch64", "unknown")
         and values[1]
         in ("unknown-linux-gnu", "unknown-linux-musl", "apple-darwin", "unknown")
+        # Validate _apple_darwin_aarch64_override was applied.
+        and triple != "aarch64-apple-darwin"
     )
 
 
+def _apple_darwin_aarch64_override(triple):
+    """
+    If using M1 ARM64 machine, still use x86_64.
+    See https://github.com/scoutapp/scout_apm_python/issues/683
+    """
+    if triple == "aarch64-apple-darwin":
+        return "x86_64-apple-darwin"
+    return triple
+
+
 def get_triple():
-    return "{arch}-{platform}".format(arch=get_arch(), platform=get_platform())
+    return _apple_darwin_aarch64_override(
+        "{arch}-{platform}".format(arch=get_arch(), platform=get_platform())
+    )
 
 
 def get_arch():
@@ -27,6 +41,8 @@ def get_arch():
         return "i686"
     elif arch == "x86_64":
         return "x86_64"
+    elif arch == "aarch64":
+        return "aarch64"
     else:
         return "unknown"
 


Test patch:
diff --git a/tests/unit/core/test_platform_detection.py b/tests/unit/core/test_platform_detection.py
--- a/tests/unit/core/test_platform_detection.py
+++ b/tests/unit/core/test_platform_detection.py
@@ -15,15 +15,39 @@ def test_get_triple():
     assert platform_detection.is_valid_triple(triple)
 
 
+@pytest.mark.parametrize(
+    "machine, system, result",
+    [
+        ("x86_64", "Darwin", "x86_64-apple-darwin"),
+        ("aarch64", "Darwin", "x86_64-apple-darwin"),
+        ("x86_64", "Linux", "x86_64-unknown-linux-musl"),
+        ("aarch64", "Linux", "aarch64-unknown-linux-musl"),
+    ],
+)
+@mock.patch("platform.machine")
+@mock.patch("platform.system")
+def test_aarch64_apple_darwin_override(
+    platform_system, platform_machine, machine, system, result
+):
+    platform_machine.return_value = machine
+    platform_system.return_value = system
+    assert platform_detection.get_triple() == result
+
+
 @pytest.mark.parametrize(
     "triple, validity",
     [
         ("x86_64-apple-darwin", True),
         ("i686-unknown-linux-gnu", True),
+        ("aarch64-unknown-linux-gnu", True),
+        ("aarch64-unknown-linux-musl", True),
+        ("x86_64-apple-darwin", True),
         ("unknown-unknown-linux-musl", True),
         ("", False),
         ("unknown", False),
         ("---", False),
+        ("i686-apple-darwin", True),
+        ("aarch64-apple-darwin", False),
     ],
 )
 def test_is_valid_triple(triple, validity):
@@ -35,6 +59,7 @@ def test_is_valid_triple(triple, validity):
     [
         ("i686", "i686"),
         ("x86_64", "x86_64"),
+        ("aarch64", "aarch64"),
         ("i386", "unknown"),
         ("arm", "unknown"),
         ("", "unknown"),