openatx / atx-agent Goto Github PK
View Code? Open in Web Editor NEWHTTP Server runs on android devices
License: Other
HTTP Server runs on android devices
License: Other
atx-agent
启动minicap
时设置的分辨率为800*800
,图像质量为80
,操作正常。
但是当我把分辨率设为1920*1080
,图像质量设置为100
时,画面就有点卡顿了,请问你那边有这个问题吗?谢谢~
atx-agent output: 2018/11/27 09:10:59 getgrop exec: "sh": executable file not found in $PATH
agent有没有机制,可以让手机的ip变了就直接重新获取ip,然后发送给server。看了只要ip变了,手机后台监听就从一个ip直接变成0.0.0.0了。
不然的话手机ip变一次也要init,重启也要init,真的感觉很麻烦
当我意图用 atx-gent 中shell 去启动 uiautomator2 的service时出现了权限不够的问题 ,adb的权限高于应用,在用adb shell 中可以正常拉起的服务,在atx-gent 的shell中却不能拉起
希望作者提供可以无线拉起 uiautomator服务的方法 ,因为可能需要在 手机中运行 自动化脚本
或许在手机端提供一个 adbserver 是一个方案 ,以下项目是adb协议的python 实现
https://github.com/google/python-adb/blob/master/adb/adb_protocol.py
log:
curl -X POST -d command="am startservice -n com.github.uiautomator/.Service" http://10.0.100.228:7912/shell
{"error":{"Stderr":null},"exitCode":255,"output":"Starting service: Intent { cmp=com.github.uiautomator/.Service }\nSecurity exception:
Permission Denial: service asks to run as user -2 but is calling from user 0;
this requires android.permission.INTERACT_ACROSS_USERS_FULL or android.permission.INTERACT_ACROSS_USERS\n\njava.lang.SecurityException: Permission Denial: service asks to run as user -2 but is calling from user 0; this requires android.permission.INTERACT_ACROSS_USERS_FULL or android.permission.INTERACT_ACROSS_USERS\n\tat com.android.server.am.UserController.handleIncomingUser(Unknown Source:232)\n\tat com.android.server.am.ActiveServices.retrieveServiceLocked(Unknown Source:83)\n\tat com.android.server.am.ActiveServices.startServiceLocked(Unknown Source:172)\n\tat com.android.server.am.ActivityManagerService.startService(Unknown Source:124)\n\tat com.android.server.am.ActivityManagerShellCommand.runStartService(Unknown Source:81)\n\tat com.android.server.am.ActivityManagerShellCommand.onCommand(Unknown Source:47)\n\tat android.os.ShellCommand.exec(Unknown Source:35)\n\tat com.android.server.am.ActivityManagerService.onShellCommand(Unknown Source:13)\n\tat android.os.Binder.shellCommand(Unknown Source:0)\n\tat android.os.Binder.onTransact(Unknown Source:127)\n\tat android.app.IActivityManager$Stub.onTransact(Unknown Source:5)\n\tat com.android.server.am.ActivityManagerService.onTransact(Unknown Source:129)\n\tat android.os.Binder.transact(Unknown Source:6)\n\tat com.daniu.O0OO0.OO00.OO0.onTransact(SourceFile:128)\n\tat android.os.Binder.execTransact(Unknown Source:63)\n"}
rt
when trying to start atx-agent on huawei honor 7 with processor Hisilicon Kirin 935, it starts failed with full error msg as following:
2017/10/18 03:19:27 main.go:541: Ignore SIGUP
2017/10/18 03:19:27 main.go:545: Kill server
2017/10/18 03:19:27 main.go:551: Get http://127.0.0.1:7912/stop: dial tcp 127.0.0.1:7912: getsockopt: connection refused
IP: 172.18.29.8
INSTRUMENTATION_STATUS: numtests=1
INSTRUMENTATION_STATUS: stream=
com.github.uiautomator.stub.Stub:
INSTRUMENTATION_STATUS: id=AndroidJUnitRunner
INSTRUMENTATION_STATUS: test=testUIAutomatorStub
INSTRUMENTATION_STATUS: class=com.github.uiautomator.stub.Stub
INSTRUMENTATION_STATUS: current=1
INSTRUMENTATION_STATUS_CODE: 1
INSTRUMENTATION_RESULT: shortMsg=Process crashed.
INSTRUMENTATION_CODE: 0
I am not sure whether this is related to processor arch, since huawei processor is aarch for arm8, and my release version is arm7.
The error is related to port usages. After killing the atx-agent, the port is available. Although I got this error, the agent was still running with port 7912.
vivo Z3 Android 8.1.0
agent 使用的自己编译的版本
手机长时间放置后,出现 agent 无法访问的情况,重新使用 shell 命令启动输出正常,但也无法访问;log 中存在大量 runtime_pollWait,查看 agent 进程信息如下
USER PID PPID VSZ RSS WCHAN ADDR S NAME
shell 20618 1 805524 6704 futex_wait_queue_me 0 S atx-agent
完整 log:atx-agent.log
when starting agent in xiaomi 3, got the error msg "Unable to find instrumentation info for: ComponentInfo{com.github.uiautomator.test/android.support.test.runner.AndroidJUnitRunner}
here is the whole msg log:
shell@pisces:/ $ cat /storage/sdcard0/atx-agent.log
2017/10/18 10:34:17 main.go:541: Ignore SIGUP
2017/10/18 10:34:17 main.go:545: Kill server
2017/10/18 10:34:17 main.go:551: Get http://127.0.0.1:7912/stop: dial tcp 127.0.0.1:7912: getsockopt: connection refused
IP: 172.18.31.22
INSTRUMENTATION_STATUS: id=ActivityManagerService
INSTRUMENTATION_STATUS: Error=Unable to find instrumentation info for: ComponentInfo{com.github.uiautomator.test/android.support.test.runner.AndroidJUnitRunner}
INSTRUMENTATION_STATUS_CODE: -1
android.util.AndroidException: INSTRUMENTATION_FAILED: com.github.uiautomator.test/android.support.test.runner.AndroidJUnitRunner
at com.android.commands.am.Am.runInstrument(Am.java:865)
at com.android.commands.am.Am.onRun(Am.java:282)
at com.android.internal.os.BaseCommand.run(BaseCommand.java:47)
at com.android.commands.am.Am.main(Am.java:76)
at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:247)
at dalvik.system.NativeStart.main(Native Method)
2017/10/18 10:34:18 main.go:188: uiautomator quit: exit status 1
I used agent with version "https://github.com/openatx/atx-agent/releases/download/0.0.5/atx-agent_0.0.5_linux_armv7.tar.gz"
my xiaomi version is MIUI 9.7.10.12 with android version 4.4.4(4.1+)
cpuinfo:
shell@pisces:/ $ cat /proc/cpuinfo
Processor : ARMv7 Processor rev 2 (v7l)
processor : 0
BogoMIPS : 1176.57
processor : 1
BogoMIPS : 1176.57
processor : 2
BogoMIPS : 1176.57
processor : 3
BogoMIPS : 1176.57
Features : swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x2
CPU part : 0xc0f
CPU revision : 2
Hardware : pisces
Revision : 0000
Serial : 062c006403430200
adb shell /data/local/tmp/atx-agent -d
2018/05/21 03:09:58 main.go:1216: server started failed, 无法启动
adb shell /data/local/tmp/atx-agent
可以正常启动
参数 -d 是什么作用呢?
我将 atx-agent 运行到本地,然后通过postman 调用install接口,在安装的时候报错,如下:
"avc: denied { read } for scontext=u:r:system_server:s0 tcontext=u:object_r:sdcardfs:s0 tclass=file permissive=0\nSystem server has no access to read file context u:object_r:sdcardfs:s0 (from path /sdcard/tmp/2022eb3db5459adebe28e8781fee02fe.apk, context u:r:system_server:s0)\nError: Unable to open file: /sdcard/tmp/2022eb3db5459adebe28e8781fee02fe.apk\nConsider using a file under /data/local/tmp/\nError: Can't open file: /sdcard/tmp/2022eb3db5459adebe28e8781fee02fe.apk\n\nException occurred while executing:\njava.lang.IllegalArgumentException: Error: Can't open file: /sdcard/tmp/2022eb3db5459adebe28e8781fee02fe.apk\n\tat com.android.server.pm.PackageManagerShellCommand.setParamsSize(PackageManagerShellCommand.java:328)\n\tat com.android.server.pm.PackageManagerShellCommand.runInstall(PackageManagerShellCommand.java:906)\n\tat com.android.server.pm.PackageManagerShellCommand.onCommand(PackageManagerShellCommand.java:158)\n\tat android.os.ShellCommand.exec(ShellCommand.java:103)\n\tat com.android.server.pm.PackageManagerService.onShellCommand(PackageManagerService.java:21260)\n\tat android.os.Binder.shellCommand(Binder.java:634)\n\tat android.os.Binder.onTransact(Binder.java:532)\n\tat android.content.pm.IPackageManager$Stub.onTransact(IPackageManager.java:2796)\n\tat com.android.server.pm.PackageManagerService.onTransact(PackageManagerService.java:3856)\n\tat android.os.Binder.execTransact(Binder.java:731)\n: exit status 255",
运行再Android手机上的http服务器,旨在希望通过Wifi控制手机,完成手机的自动化功能。
WebSocket
长时间运行,抛出错误,查看了下,进程也已经退出,也就是daemon
没有进行自动重启。
下面是详细日志,我分析了下,感觉都没太大问题,不知道是不是哪里的锁冲突了。
10.10.10.166 - - [07/Dec/2018:16:38:52 +0800] "POST /jsonrpc/0 HTTP/1.1" 200 89
2018/12/07 16:38:52 startservice com.github.uiautomator/.Service
fatal error: schedule: holding locks
runtime stack:
runtime.throw(0x51fe29, 0x17)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/runtime/panic.go:608 +0x5c
runtime.schedule()
/home/travis/.gimme/versions/go1.11.linux.amd64/src/runtime/proc.go:2561 +0x2f0
runtime.park_m(0x2a808c0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/runtime/proc.go:2676 +0x8c
runtime.mcall(0x28383c0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/runtime/asm_arm.s:289 +0x5c
goroutine 1 [IO wait]:
internal/poll.runtime_pollWait(0x98365fc0, 0x72, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/runtime/netpoll.go:173 +0x44
internal/poll.(*pollDesc).wait(0x28a6334, 0x72, 0x2919c00, 0x0, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/internal/poll/fd_poll_runtime.go:85 +0x7c
internal/poll.(*pollDesc).waitRead(0x28a6334, 0xffffff00, 0x0, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/internal/poll/fd_poll_runtime.go:90 +0x2c
internal/poll.(*FD).Accept(0x28a6320, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/internal/poll/fd_unix.go:384 +0x17c
net.(*netFD).accept(0x28a6320, 0x2b06b60, 0x2ae2000, 0x29de0d8)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/fd_unix.go:238 +0x20
net.(*TCPListener).accept(0x280cf20, 0x2ae2040, 0xa319900, 0x4335c)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/tcpsock_posix.go:139 +0x20
net.(*TCPListener).Accept(0x280cf20, 0x2ca0ca4, 0xc, 0x28000e0, 0x29fa00)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/tcpsock.go:260 +0x3c
net/http.(*Server).Serve(0x29d6680, 0x5bfb38, 0x280cf20, 0x0, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/http/server.go:2826 +0x1e0
main.(*Server).Serve(0x29b2138, 0x5bfb38, 0x280cf20, 0x281aa5d, 0x29b2138)
/home/travis/gopath/src/github.com/openatx/atx-agent/restapi.go:956 +0x30
main.main()
/home/travis/gopath/src/github.com/openatx/atx-agent/main.go:608 +0x12e8
goroutine 5 [syscall, 3020 minutes]:
os/signal.signal_recv(0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/runtime/sigqueue.go:139 +0x130
os/signal.loop()
/home/travis/.gimme/versions/go1.11.linux.amd64/src/os/signal/signal_unix.go:23 +0x14
created by os/signal.init.0
/home/travis/.gimme/versions/go1.11.linux.amd64/src/os/signal/signal_unix.go:29 +0x30
goroutine 14 [syscall, 3020 minutes]:
syscall.Syscall6(0x118, 0x1, 0x374a, 0x2837eb4, 0x1000004, 0x0, 0x0, 0x2801b68, 0x2, 0x3)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/syscall/asm_linux_arm.s:45 +0x8
os.(*Process).blockUntilWaitable(0x28a4240, 0x1, 0x4a5601, 0x18ea8)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/os/wait_waitid.go:31 +0x64
os.(*Process).wait(0x28a4240, 0x280a998, 0x2837fdc, 0x29a0000)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/os/exec_unix.go:22 +0x2c
os.(*Process).Wait(0x28a4240, 0x0, 0x2907b40, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/os/exec.go:125 +0x1c
os/exec.(*Cmd).Wait(0x2884840, 0x3179c4, 0x1)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/os/exec/exec.go:465 +0x40
os/exec.(*Cmd).Wait-fm(0x2907bc0, 0x2837fdc)
/home/travis/gopath/src/github.com/openatx/atx-agent/cmdctrl/cmdctrl.go:228 +0x1c
github.com/openatx/atx-agent/cmdctrl.goFunc.func1(0x2907c80, 0x280d110)
/home/travis/gopath/src/github.com/openatx/atx-agent/cmdctrl/cmdctrl.go:34 +0x1c
created by github.com/openatx/atx-agent/cmdctrl.goFunc
/home/travis/gopath/src/github.com/openatx/atx-agent/cmdctrl/cmdctrl.go:33 +0x48
goroutine 19 [select, 3020 minutes]:
github.com/openatx/atx-agent/cmdctrl.(*processKeeper).start.func1(0x29d6500)
/home/travis/gopath/src/github.com/openatx/atx-agent/cmdctrl/cmdctrl.go:229 +0x3cc
created by github.com/openatx/atx-agent/cmdctrl.(*processKeeper).start
/home/travis/gopath/src/github.com/openatx/atx-agent/cmdctrl/cmdctrl.go:208 +0xd0
goroutine 33 [select]:
github.com/codeskyblue/heartbeat.(*Client).beatLoop(0x2926060, 0x5c0038, 0x29260e0, 0x540be400, 0x2, 0x2ad6030, 0xa, 0x0, 0x0)
/home/travis/gopath/pkg/mod/github.com/codeskyblue/[email protected]/heartbeat.go:197 +0x124
github.com/codeskyblue/heartbeat.(*Client).Beat.func1(0x2926060, 0x540be400, 0x2, 0x5c0038, 0x29260e0)
/home/travis/gopath/pkg/mod/github.com/codeskyblue/[email protected]/heartbeat.go:178 +0x94
created by github.com/codeskyblue/heartbeat.(*Client).Beat
/home/travis/gopath/pkg/mod/github.com/codeskyblue/[email protected]/heartbeat.go:159 +0xac
goroutine 21 [syscall]:
syscall.Syscall6(0x118, 0x1, 0x7dc4, 0x2d675ac, 0x1000004, 0x0, 0x0, 0x4f03c0, 0x13b3c, 0x2afcfa8)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/syscall/asm_linux_arm.s:45 +0x8
os.(*Process).blockUntilWaitable(0x29fc150, 0x29fc150, 0x0, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/os/wait_waitid.go:31 +0x64
os.(*Process).wait(0x29fc150, 0x1, 0x2afcfa0, 0x2a69840)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/os/exec_unix.go:22 +0x2c
os.(*Process).Wait(0x29fc150, 0x53b5c8, 0x53b5cc, 0x53b5c4)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/os/exec.go:125 +0x1c
os/exec.(*Cmd).Wait(0x2c33340, 0x0, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/os/exec/exec.go:465 +0x40
os/exec.(*Cmd).Run(0x2c33340, 0x53b8b4, 0x2af90e0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/os/exec/exec.go:309 +0x44
main.Command.Run(0x2a69820, 0x4, 0x4, 0xb2c97000, 0x8b, 0x1, 0x5bcba8, 0x2ae2060, 0x5bcba8, 0x2ae2060, ...)
/home/travis/gopath/src/github.com/openatx/atx-agent/utils.go:86 +0x78
main.Command.CombinedOutput(0x2a69820, 0x4, 0x4, 0xb2c97000, 0x8b, 0x1, 0x5bcba8, 0x2ae2060, 0x5bcba8, 0x2ae2060, ...)
/home/travis/gopath/src/github.com/openatx/atx-agent/utils.go:101 +0x48
main.runShell(0x2a69820, 0x4, 0x4, 0x2afa000, 0x41, 0x600, 0x0, 0x0)
/home/travis/gopath/src/github.com/openatx/atx-agent/utils.go:117 +0x5c
main.(*Server).initHTTPServer.func17(0x29ac570)
/home/travis/gopath/src/github.com/openatx/atx-agent/restapi.go:367 +0x48
created by main.(*Server).initHTTPServer
/home/travis/gopath/src/github.com/openatx/atx-agent/restapi.go:364 +0x698
goroutine 38 [chan receive, 3020 minutes]:
main.main.func3(0x2a8bc40, 0x281aa5d, 0x29b2138)
/home/travis/gopath/src/github.com/openatx/atx-agent/main.go:587 +0x74
created by main.main
/home/travis/gopath/src/github.com/openatx/atx-agent/main.go:586 +0x12cc
goroutine 650701 [IO wait]:
internal/poll.runtime_pollWait(0x981f1a00, 0x72, 0xe4938)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/runtime/netpoll.go:173 +0x44
internal/poll.(*pollDesc).wait(0x2cb8154, 0x72, 0xffffff01, 0x5be4c8, 0x8f0160)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/internal/poll/fd_poll_runtime.go:85 +0x7c
internal/poll.(*pollDesc).waitRead(0x2cb8154, 0x2ad2401, 0x200, 0x200)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/internal/poll/fd_poll_runtime.go:90 +0x2c
internal/poll.(*FD).Read(0x2cb8140, 0x2ad2400, 0x200, 0x200, 0x0, 0x0, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/internal/poll/fd_unix.go:169 +0x14c
os.(*File).read(0x2afcf88, 0x2ad2400, 0x200, 0x200, 0x2ad2400, 0x0, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/os/file_unix.go:249 +0x3c
os.(*File).Read(0x2afcf88, 0x2ad2400, 0x200, 0x200, 0x98321018, 0x1, 0x1)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/os/file.go:108 +0x4c
bytes.(*Buffer).ReadFrom(0x2ae2060, 0x5bd838, 0x2afcf88, 0x98321018, 0x2ae2060, 0x2a80901, 0x2d747c4)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/bytes/buffer.go:206 +0xa8
io.copyBuffer(0x5bcba8, 0x2ae2060, 0x5bd838, 0x2afcf88, 0x0, 0x0, 0x0, 0x2d747ac, 0x2d747a4, 0x2, ...)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/io/io.go:388 +0x300
io.Copy(0x5bcba8, 0x2ae2060, 0x5bd838, 0x2afcf88, 0x1, 0x0, 0x0, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/io/io.go:364 +0x48
os/exec.(*Cmd).writerDescriptor.func1(0x65aec, 0x53b6dc)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/os/exec/exec.go:279 +0x38
os/exec.(*Cmd).Start.func1(0x2c33340, 0x29ac870)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/os/exec/exec.go:400 +0x1c
created by os/exec.(*Cmd).Start
/home/travis/.gimme/versions/go1.11.linux.amd64/src/os/exec/exec.go:399 +0x414
goroutine 650706 [IO wait]:
internal/poll.runtime_pollWait(0x981f1e80, 0x72, 0xe4938)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/runtime/netpoll.go:173 +0x44
internal/poll.(*pollDesc).wait(0x2b6c154, 0x72, 0xffffff00, 0x5be4c8, 0x8f0160)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/internal/poll/fd_poll_runtime.go:85 +0x7c
internal/poll.(*pollDesc).waitRead(0x2b6c154, 0x2982000, 0x1000, 0x1000)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/internal/poll/fd_poll_runtime.go:90 +0x2c
internal/poll.(*FD).Read(0x2b6c140, 0x2982000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/internal/poll/fd_unix.go:169 +0x14c
net.(*netFD).Read(0x2b6c140, 0x2982000, 0x1000, 0x1000, 0x2c35180, 0x4, 0x141a4)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/fd_unix.go:202 +0x38
net.(*conn).Read(0x29d4088, 0x2982000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/net.go:177 +0x58
net/http.(*persistConn).Read(0x2b88140, 0x2982000, 0x1000, 0x1000, 0x2b1570, 0x2b41a80, 0x2ba3620)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/http/transport.go:1497 +0x170
bufio.(*Reader).fill(0x29ae120)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/bufio/bufio.go:100 +0x10c
bufio.(*Reader).Peek(0x29ae120, 0x1, 0x0, 0x0, 0x1, 0x2ace140, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/bufio/bufio.go:132 +0x2c
net/http.(*persistConn).readLoop(0x2b88140)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/http/transport.go:1645 +0x164
created by net/http.(*Transport).dialConn
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/http/transport.go:1338 +0x7f8
goroutine 619202 [IO wait]:
internal/poll.runtime_pollWait(0x981f1e00, 0x72, 0xe4938)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/runtime/netpoll.go:173 +0x44
internal/poll.(*pollDesc).wait(0x28a6424, 0x72, 0xffffff00, 0x5be4c8, 0x8f0160)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/internal/poll/fd_poll_runtime.go:85 +0x7c
internal/poll.(*pollDesc).waitRead(0x28a6424, 0x2be6000, 0x1000, 0x1000)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/internal/poll/fd_poll_runtime.go:90 +0x2c
internal/poll.(*FD).Read(0x28a6410, 0x2be6000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/internal/poll/fd_unix.go:169 +0x14c
net.(*netFD).Read(0x28a6410, 0x2be6000, 0x1000, 0x1000, 0x291c540, 0x4, 0x141a4)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/fd_unix.go:202 +0x38
net.(*conn).Read(0x2a322e8, 0x2be6000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/net.go:177 +0x58
net/http.(*persistConn).Read(0x2b880a0, 0x2be6000, 0x1000, 0x1000, 0x2b1570, 0x2b41180, 0x288de20)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/http/transport.go:1497 +0x170
bufio.(*Reader).fill(0x2d64330)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/bufio/bufio.go:100 +0x10c
bufio.(*Reader).Peek(0x2d64330, 0x1, 0x0, 0x0, 0x1, 0x2b41780, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/bufio/bufio.go:132 +0x2c
net/http.(*persistConn).readLoop(0x2b880a0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/http/transport.go:1645 +0x164
created by net/http.(*Transport).dialConn
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/http/transport.go:1338 +0x7f8
goroutine 650707 [select]:
net/http.(*persistConn).writeLoop(0x2b88140)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/http/transport.go:1885 +0xb8
created by net/http.(*Transport).dialConn
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/http/transport.go:1339 +0x814
goroutine 650690 [IO wait]:
internal/poll.runtime_pollWait(0x98365f40, 0x72, 0xe4938)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/runtime/netpoll.go:173 +0x44
internal/poll.(*pollDesc).wait(0x2a96834, 0x72, 0xffffff00, 0x5be4c8, 0x8f0160)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/internal/poll/fd_poll_runtime.go:85 +0x7c
internal/poll.(*pollDesc).waitRead(0x2a96834, 0x2b9c000, 0x1000, 0x1000)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/internal/poll/fd_poll_runtime.go:90 +0x2c
internal/poll.(*FD).Read(0x2a96820, 0x2b9c000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/internal/poll/fd_unix.go:169 +0x14c
net.(*netFD).Read(0x2a96820, 0x2b9c000, 0x1000, 0x1000, 0xffffffff, 0x0, 0x2977e4)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/fd_unix.go:202 +0x38
net.(*conn).Read(0x2afcb90, 0x2b9c000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/net.go:177 +0x58
net/http.(*connReader).Read(0x2c5a060, 0x2b9c000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/http/server.go:786 +0x14c
bufio.(*Reader).fill(0x29ae000)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/bufio/bufio.go:100 +0x10c
bufio.(*Reader).Peek(0x29ae000, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/bufio/bufio.go:132 +0x2c
net/http.(*conn).readRequest(0x2ae2000, 0x5c0038, 0x2c5a020, 0x0, 0x0, 0x0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/http/server.go:963 +0xa84
net/http.(*conn).serve(0x2ae2000, 0x5c0038, 0x2c5a020)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/http/server.go:1788 +0x3f0
created by net/http.(*Server).Serve
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/http/server.go:2851 +0x290
goroutine 619203 [select]:
net/http.(*persistConn).writeLoop(0x2b880a0)
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/http/transport.go:1885 +0xb8
created by net/http.(*Transport).dialConn
/home/travis/.gimme/versions/go1.11.linux.amd64/src/net/http/transport.go:1339 +0x814
按照安装文档步骤执行,报如下错误
$ git clone https://github.com/openatx/atx-agent
$ cd atx-agent
$ export GO111MODULE=on
$ go generate
go: finding github.com/qiniu/log v0.0.0-20140728010919-a304a74568d6
go: github.com/qiniu/[email protected]: git fetch -f https://github.com/qiniu/log refs/heads/:refs/heads/ refs/tags/:refs/tags/ in D:\mygo\pkg\mod\cache\vcs\0fdd75578a3b407f676ed893d01f93d14d2c857ed66a82a1deffa8f5b4014197: exit status 128:
remote: Repository not found.
fatal: repository 'https://github.com/qiniu/log/' not found
go: error loading module requirements
0.4.3版本,使用源码下载编译的atx-agent有13+MB,比下载的atx-agent_0.4.3_linux_armv7.tar.gz大近5MB。是我编译的方式有问题吗?
我是参考的build.sh里面的命令:
go generate
GOOS=linux GOARCH=arm go build -tags vfs
Hi,
I'm performing a heavy install/uninstall app tests with atx-agent, which involves lots of repeatedly push/install/uninstall operation. Usually atx-agent die with no more than 100 apps, today after I added a recoverwrap func around every http handler, agent keeps alive after 1000 apps and it's still working.
I can't figure out why, maybe lust lucky time. I posted it here, hope someone else could also give it a try, as agents stableness appears to be a problem when trying to run some CI tasks with atx-agents. Even after upgrade to 0.4.0 with new go daemon, it dies from time to time.
The wrap method:
func RecoverWrap(f func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
//var err error
defer func() {
if e := recover(); e != nil {
log.Println("Detect panic !!!", e)
ioutil.WriteFile("/sdcard/atx-panic.txt", []byte(fmt.Sprintf(
"Time: %s\n%v", time.Now().Format("2006-01-02 15:04:05"),
e)), 0644)
}
}()
f(w, r)
}
}
func RecoverWrapNew(f func(w http.ResponseWriter, r *http.Request, ws *websocket.Conn)) func(w http.ResponseWriter, r *http.Request, ws *websocket.Conn) {
return func(w http.ResponseWriter, r *http.Request, ws *websocket.Conn){
//var err error
defer func() {
if e := recover(); e != nil {
log.Println("Detect panic !!!", e)
ioutil.WriteFile("/sdcard/atx-panic.txt", []byte(fmt.Sprintf(
"Time: %s\n%v", time.Now().Format("2006-01-02 15:04:05"),
e)), 0644)
}
}()
f(w, r, ws)
}
}
And I use it like this(attempt to collect more info):
m.HandleFunc("/version", RecoverWrap(func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, version)
}))
m.HandleFunc("/upload/{target:.*}", RecoverWrap(func(w http.ResponseWriter, r *http.Request) {
...
m.HandleFunc("/minitouch", singleFightNewerWebsocket(RecoverWrapNew(func(w http.ResponseWriter, r *http.Request, ws *websocket.Conn) {
......
BTW, during my tests there is no atx-panic.txt created, so there should be no panic captured, and the RecoveWrap seems to have no effect. (Could it because of defer()?).
I'll do more tests tomorrow.
Cheers.
Hi.
I got an error with atx-agent
when I try to test code using uiautomator2
.
When I use app_install
method, the error is :
Traceback (most recent call last):
File "example.py", line 11, in <module>
d.app_install('http://example.org/AliExpress%20Smarter%20Shopping%20Better%20Living_v6.15.0_apkpure.com.apk')
File "C:\Python27\lib\site-packages\uiautomator2\__init__.py", line 689, in app_install
return self._wait_install_finished(id, installing_callback)
File "C:\Python27\lib\site-packages\uiautomator2\__init__.py", line 744, in _wait_install_finished
raise RuntimeError("error", jdata.get('error'))
RuntimeError: ('error', u'Get http://example.org/AliExpress%20Smarter%20Shopping%20Better%20Living_v6.15.0_apkpure.com.apk: dial tcp: lookup example.org on [::1]:53: read udp [::1]:42576->[::1]:53: read: connection refused')
Is there any solution about this?
I saw the dns server has been fixed at /dns.go
. Is it related with the error?
在某些内部局域网WiFi网络环境下,手机获取的DNS有三个,DNS1为ipv6本地地址,但是现阶段公网IPv6没有普及,导致下载minicap、minitouch时DNS报错:
Get https://github.com/codeskyblue/stf-binaries/raw/master/node_modules/minicap-prebuilt/prebuilt/arm64-v8a/bin/minicap: dial tcp: lookup github.com on [::1]:53: dial udp: address fe80::5e63:bfff:fed0:77ff:53: too many colons in address
手机DNS如下(爱快软路由+UBNT UAP-AC-LITE):
shell@A59:/ $ getprop | grep net.dns
[net.dns1]: [fe80::5e63:bfff:fed0:77ff]
[net.dns2]: [211.140.13.188]
[net.dns3]: [211.140.188.188]
shell@A59:/ $
shell@A59:/ $
因为不是每个测试人员都可以去维护修改公司网络,是否可以优化dns.go的这个逻辑(现有通过getprop获得net.dns1,dns1不为空情况下,使用手机的dns1。没学过go语言:(,不知理解是否有误。):
func (c *dnsSmartClient) Dial(ctx context.Context, network, address string) (conn net.Conn, err error) {
dns1 := getProperty("net.dns1")
if dns1 == "" {
// 国内DNS列表: https://www.zhihu.com/question/32229915
dns1 = "114.114.114.114"
}
log.Println("dns resolve", dns1)
return c.dialer.DialContext(ctx, "udp", dns1+":53")
}
建议加个正则(或地址有冒号)判断dns1是否为ipv6,是的话,使用114DNS或者当net.dns1位IPV6地址时增加net.dns2的判断。
临时解决方案:
1、通过移动数据安装minicap(需手机adb shell支持curl);
2、或找个手机发移动热点,手机和电脑连接同一个数据热点,执行curl命令安装minicap。
atx-agent 运行在手机上,通过curl调用install接口安装apk报错:
"avc: denied { read } for scontext=u:r:system_server:s0 tcontext=u:object_r:sdcardfs:s0 tclass=file permissive=0\nSystem server has no access to read file context u:object_r:sdcardfs:s0 (from path /sdcard/tmp/2022eb3db5459adebe28e8781fee02fe.apk, context u:r:system_server:s0)\nError: Unable to open file: /sdcard/tmp/2022eb3db5459adebe28e8781fee02fe.apk\nConsider using a file under /data/local/tmp/\nError: Can't open file: /sdcard/tmp/2022eb3db5459adebe28e8781fee02fe.apk\n\nException occurred while executing:\njava.lang.IllegalArgumentException: Error: Can't open file: /sdcard/tmp/2022eb3db5459adebe28e8781fee02fe.apk\n\tat com.android.server.pm.PackageManagerShellCommand.setParamsSize(PackageManagerShellCommand.java:328)\n\tat com.android.server.pm.PackageManagerShellCommand.runInstall(PackageManagerShellCommand.java:906)\n\tat com.android.server.pm.PackageManagerShellCommand.onCommand(PackageManagerShellCommand.java:158)\n\tat android.os.ShellCommand.exec(ShellCommand.java:103)\n\tat com.android.server.pm.PackageManagerService.onShellCommand(PackageManagerService.java:21260)\n\tat android.os.Binder.shellCommand(Binder.java:634)\n\tat android.os.Binder.onTransact(Binder.java:532)\n\tat android.content.pm.IPackageManager$Stub.onTransact(IPackageManager.java:2796)\n\tat com.android.server.pm.PackageManagerService.onTransact(PackageManagerService.java:3856)\n\tat android.os.Binder.execTransact(Binder.java:731)\n: exit status 255",
希望atx-agent统一增加请求和返回结果的日志信息,开关可控。
方便定位类似agent未收到预期请求,agent没返回adbd或者uiautomator的执行结果。
For Mi-4c(Android 7.0), after successfully connected to atx-server, tap on use, a new tab opens and perform some operations like swipe and click, nothing happens.
Here is log from atx-agent(version 0.3.2):
2018/05/31 07:58:18 tunnelproxy.go:159: res err -------------------
<nil> Post http://10.2.200.84:8000/devices/69512948-20:82:c0:65:09:28-Mi-4c/info: EOF
2018/05/31 07:58:29 main.go:953: minitouch connection: 10.2.200.84:55765
2018/05/31 07:58:29 main.go:972: dial @minitouch error: dial unix @minitouch: connect: connection refused, wait 0.5s
2018/05/31 07:58:29 cmdctrl.go:27: DEBUG start args: [/data/local/tmp/minitouch], env: []
2018/05/31 07:58:29 cmdctrl.go:27: DEBUG program pid: 1770
2018/05/31 07:58:29 cmdctrl.go:27: DEBUG cmd wait err: exit status 1
2018/05/31 07:58:29 cmdctrl.go:27: DEBUG idle for 500ms
2018/05/31 07:58:29 main.go:1033: minicap connection: 10.2.200.84:55766
2018/05/31 07:58:29 cmdctrl.go:27: DEBUG start args: [/data/local/tmp/minicap -S -P 1080x1920@800x800/0], env: [LD_LIBRARY_PATH=/data/local/tmp]
2018/05/31 07:58:29 main.go:1049: dial @minicap err: dial unix @minicap: connect: connection refused, wait 0.5s
2018/05/31 07:58:29 cmdctrl.go:27: DEBUG program pid: 1771
2018/05/31 07:58:29 main.go:972: dial @minitouch error: dial unix @minitouch: connect: connection refused, wait 0.5s
2018/05/31 07:58:29 cmdctrl.go:27: DEBUG start args: [/data/local/tmp/minitouch], env: []
2018/05/31 07:58:29 cmdctrl.go:27: DEBUG program pid: 1790
2018/05/31 07:58:29 cmdctrl.go:27: DEBUG cmd wait err: exit status 1
2018/05/31 07:58:29 cmdctrl.go:27: DEBUG idle for 500ms
2018/05/31 07:58:30 main.go:972: dial @minitouch error: dial unix @minitouch: connect: connection refused, wait 0.5s
2018/05/31 07:58:30 cmdctrl.go:27: DEBUG start args: [/data/local/tmp/minitouch], env: []
2018/05/31 07:58:30 cmdctrl.go:27: DEBUG program pid: 1795
2018/05/31 07:58:30 cmdctrl.go:27: DEBUG cmd wait err: exit status 1
2018/05/31 07:58:30 cmdctrl.go:27: DEBUG idle for 500ms
2018/05/31 07:58:30 main.go:972: dial @minitouch error: dial unix @minitouch: connect: connection refused, wait 0.5s
2018/05/31 07:58:30 cmdctrl.go:27: DEBUG start args: [/data/local/tmp/minitouch], env: []
2018/05/31 07:58:30 cmdctrl.go:27: DEBUG program pid: 1795
2018/05/31 07:58:30 cmdctrl.go:27: DEBUG cmd wait err: exit status 1
2018/05/31 07:58:30 cmdctrl.go:27: DEBUG idle for 500ms
2018/05/31 07:58:30 main.go:972: dial @minitouch error: dial unix @minitouch: connect: connection refused, wait 0.5s
2018/05/31 07:58:30 cmdctrl.go:27: DEBUG start args: [/data/local/tmp/minitouch], env: []
2018/05/31 07:58:30 cmdctrl.go:27: DEBUG program pid: 1796
2018/05/31 07:58:30 cmdctrl.go:27: DEBUG cmd wait err: exit status 1
2018/05/31 07:58:30 cmdctrl.go:27: DEBUG idle for 500ms
2018/05/31 07:58:31 main.go:972: dial @minitouch error: dial unix @minitouch: connect: connection refused, wait 0.5s
2018/05/31 07:58:31 cmdctrl.go:27: DEBUG program finished
2018/05/31 07:58:31 main.go:972: dial @minitouch error: dial unix @minitouch: connect: connection refused, wait 0.5s
2018/05/31 07:58:32 main.go:972: dial @minitouch error: dial unix @minitouch: connect: connection refused, wait 0.5s
2018/05/31 07:58:32 main.go:972: dial @minitouch error: dial unix @minitouch: connect: connection refused, wait 0.5s
2018/05/31 07:58:33 main.go:972: dial @minitouch error: dial unix @minitouch: connect: connection refused, wait 0.5s
2018/05/31 07:58:33 main.go:972: dial @minitouch error: dial unix @minitouch: connect: connection refused, wait 0.5s
2018/05/31 07:58:34 main.go:972: dial @minitouch error: dial unix @minitouch: connect: connection refused, wait 0.5s
Experiment
ticker := time.NewTicker(time.Millisecond * 200) // 20fps
defer ticker.Stop()
var jpgData []byte
SEND_LOOP:
for {
select {
case <-ticker.C:
if jpgData != nil { // jpeg data
if err := wsWrite(websocket.BinaryMessage, jpgData); err != nil {
break SEND_LOOP
}
jpgData = nil
}
case data := <-dataC:
if string(data[:2]) == "\xff\xd8" {
jpgData = data
} else {
if err := wsWrite(websocket.TextMessage, data); err != nil {
break SEND_LOOP
}
}
}
}
查看日志如下
2018/05/30 11:53:51 main.go:1292: Ignore SIGHUP
2018/05/30 11:53:51 main.go:1296: Kill server
2018/05/30 11:53:51 main.go:1302: Get http://127.0.0.1:7912/stop: dial tcp 127.0.0.1:7912: connect: connection refused
atx-agent version 0.3.0
Listen on http://10.70.111.53:7912
2018/05/30 11:53:52 cmdctrl.go:27: DEBUG start args: [am instrument -w -r -e debug false -e class com.github.uiautomator.stub.Stub com.github.uiautomator.test/android.support.test.runner.AndroidJUnitRunner], env: []
2018/05/30 11:53:52 cmdctrl.go:27: DEBUG program pid: 27574
INSTRUMENTATION_STATUS: numtests=1
INSTRUMENTATION_STATUS: stream=
com.github.uiautomator.stub.Stub:
INSTRUMENTATION_STATUS: id=AndroidJUnitRunner
INSTRUMENTATION_STATUS: test=testUIAutomatorStub
INSTRUMENTATION_STATUS: class=com.github.uiautomator.stub.Stub
INSTRUMENTATION_STATUS: current=1
INSTRUMENTATION_STATUS_CODE: 1
2018/05/30 11:56:20 reverseproxy.go:321: http: proxy error: dial tcp 127.0.0.1:9008: i/o timeout
2018/05/30 11:56:28 reverseproxy.go:321: http: proxy error: dial tcp 127.0.0.1:9008: i/o timeout
2018/05/30 11:56:34 reverseproxy.go:321: http: proxy error: dial tcp 127.0.0.1:9008: i/o timeout
2018/05/30 11:56:40 reverseproxy.go:321: http: proxy error: dial tcp 127.0.0.1:9008: i/o timeout
2018/05/30 11:56:45 reverseproxy.go:321: http: proxy error: dial tcp 127.0.0.1:9008: i/o timeout
2018/05/30 11:56:46 main.go:1203: screenshot[minicap] error: exit status 127
2018/05/30 11:56:51 reverseproxy.go:321: http: proxy error: dial tcp 127.0.0.1:9008: i/o timeout
2018/05/30 11:57:01 main.go:1203: screenshot[minicap] error: exit status 127
2018/05/30 11:57:06 reverseproxy.go:321: http: proxy error: dial tcp 127.0.0.1:9008: i/o timeout
2018/05/30 11:57:18 reverseproxy.go:321: http: proxy error: dial tcp 127.0.0.1:9008: i/o timeout
可以防止atx-agent长期占用 accessibility服务
这个会被系统杀死吗。
重现步骤:
1.启动agent,注册手机设备后可以在atx-server的设备列表中显示
2.将USB线拔下,然后关闭屏幕等待一会
3.设备列表中显示的手机将会消失
4.请求agent的info接口,能够正常返回agent信息
curl $device_url/raw/sdcard/1.txt
结果为 404 page not found
提个建议,个人感觉应该着重atx-agent的开发,脚本端用户可以根据自己情况使用各种语言编写测试脚本即可。作者不必对脚本端做太多封装(主要小弟不会py,虽然作者用py对脚本端做了很多封装,对我来说使用起来还是有一定学习成本,所以还是准备用自己熟悉的js或这java编写测试脚本)
uidevice.app_start(self.dbtask.package, self._crawlerConfig.startActivity, stop=stop)
uidevice.app_start(self.dbtask.package, self._crawlerConfig.startActivity, stop=stop)
记得agent是定时heartbeat 所以atxserver地址搞一个全局变量 然后join接口直接改 并trigger一次heartbeat通讯
另外建议加一个GET是获取uiautomator的当前状态
https://github.com/openatx/atx-agent/blob/master/main.go#L760
各一个running 判断if,可惜不会写go
下载包 atx-agent_0.4.4_linux_armv7.tar.gz
运行
$adb shell /data/local/tmp/atx-agent -d
usage: atx-agent [<flags>] <command> [<args> ...]
Flags:
-h, --help Show context-sensitive help (also try --help-long and
--help-man).
-d, --daemon run in daemon mode
-p, --port=7912 listen port
--stop stop server
--log=LOG log file path when in daemon mode
-t, --server=SERVER server url
--nouia do not start uiautoamtor when start
-v, --version Show application version.
Commands:
help [<command>...]
Show help.
curl [<flags>] <url>
simulate curl command
同样环境下 0.4.3版本正常
atx-agent.log
018/10/30 04:58:14 http: Accept error: accept tcp [::]:7912: accept4: too many open files; retrying in 1s
2018/10/30 04:58:15 http: Accept error: accept tcp [::]:7912: accept4: too many open files; retrying in 1s
2018/10/30 04:58:16 http: Accept error: accept tcp [::]:7912: accept4: too many open files; retrying in 1s
2018/10/30 04:58:17 http: Accept error: accept tcp [::]:7912: accept4: too many open files; retrying in 1s
2018/10/30 04:58:18 http: Accept error: accept tcp [::]:7912: accept4: too many open files; retrying in 1s
2018/10/30 04:58:19 http: Accept error: accept tcp [::]:7912: accept4: too many open files; retrying in 1s
2018/10/30 04:58:20 http: Accept error: accept tcp [::]:7912: accept4: too many open files; retrying in 1s
2018/10/30 04:58:21 http: Accept error: accept tcp [::]:7912: accept4: too many open files; retrying in 1s
2018/10/30 04:58:22 http: Accept error: accept tcp [::]:7912: accept4: too many open files; retrying in 1s
2018/10/30 04:58:23 http: Accept error: accept tcp [::]:7912: accept4: too many open files; retrying in 1s
2018/10/30 04:58:24 http: Accept error: accept tcp [::]:7912: accept4: too many open files; retrying in 1s
2018/10/30 04:58:25 http: Accept error: accept tcp [::]:7912: accept4: too many open files; retrying in 1s
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.