Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
weixin_47647075
emmagee
提交
8fdb357f
E
emmagee
项目概览
weixin_47647075
/
emmagee
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
E
emmagee
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
8fdb357f
编写于
9月 11, 2013
作者:
H
hzhuangqingbin
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
新增性能数据收集接口和ant自动打包文件build.xml
上级
d4ec6c24
变更
9
隐藏空白更改
内联
并排
Showing
9 changed file
with
1030 addition
and
85 deletion
+1030
-85
.classpath
.classpath
+12
-11
build.xml
build.xml
+47
-0
gen/com/netease/qa/emmagee/R.java
gen/com/netease/qa/emmagee/R.java
+81
-72
src/com/netease/emmagee/performance/CpuInfo.java
src/com/netease/emmagee/performance/CpuInfo.java
+214
-0
src/com/netease/emmagee/performance/MemoryInfo.java
src/com/netease/emmagee/performance/MemoryInfo.java
+129
-0
src/com/netease/emmagee/performance/PerformanceMonitor.java
src/com/netease/emmagee/performance/PerformanceMonitor.java
+369
-0
src/com/netease/emmagee/performance/TrafficInfo.java
src/com/netease/emmagee/performance/TrafficInfo.java
+78
-0
src/com/netease/qa/emmagee/utils/CpuInfo.java
src/com/netease/qa/emmagee/utils/CpuInfo.java
+52
-2
src/com/netease/qa/emmagee/utils/TrafficInfo.java
src/com/netease/qa/emmagee/utils/TrafficInfo.java
+48
-0
未找到文件。
.classpath
浏览文件 @
8fdb357f
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry
kind=
"src"
path=
"src"
/>
<classpathentry
kind=
"src"
path=
"gen"
/>
<classpathentry
kind=
"src"
path=
"libs"
/>
<classpathentry
kind=
"con"
path=
"com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"
/>
<classpathentry
kind=
"lib"
path=
"libs/activation.jar"
/>
<classpathentry
kind=
"lib"
path=
"libs/mail.jar"
/>
<classpathentry
kind=
"lib"
path=
"libs/additionnal.jar"
/>
<classpathentry
kind=
"output"
path=
"bin/classes"
/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry
kind=
"src"
path=
"src"
/>
<classpathentry
kind=
"src"
path=
"gen"
/>
<classpathentry
kind=
"src"
path=
"libs"
/>
<classpathentry
kind=
"con"
path=
"com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"
/>
<classpathentry
kind=
"lib"
path=
"libs/activation.jar"
/>
<classpathentry
kind=
"lib"
path=
"libs/mail.jar"
/>
<classpathentry
kind=
"lib"
path=
"libs/additionnal.jar"
/>
<classpathentry
kind=
"lib"
path=
"libs/android-support-v4.jar"
/>
<classpathentry
kind=
"output"
path=
"bin/classes"
/>
</classpath>
build.xml
0 → 100644
浏览文件 @
8fdb357f
<?xml version="1.0" encoding="UTF-8"?>
<project
name=
"PerformanceMonitor"
default=
"build"
basedir=
"."
>
<property
name=
"src"
value=
"src"
/>
<property
name=
"gen"
value=
"gen"
/>
<property
name=
"classes"
value=
"classes"
/>
<property
name=
"jarFile"
value=
"PerformanceMonitor.jar"
/>
<!-- libs包下的jar包和android目录下的jar包,后续可以改进用环境变量来替代 -->
<property
environment=
"SystemVariable"
/>
<!-- 后续可通过换届变量来指定jar文件 -->
<path
id=
"Referenced libs"
>
<fileset
dir=
"libs"
>
<include
name=
"*.jar"
/>
</fileset>
<fileset
dir=
"C:\Program Files\Android\android-sdk\platforms"
>
<include
name=
"android-10\android.jar"
/>
</fileset>
</path>
<target
name=
"clean"
description=
"清除目录和jar文件"
>
<delete
dir=
"${classes}"
/>
<delete
file=
"${jarFile}"
/>
</target>
<target
name=
"init"
depends=
"clean"
description=
"新建目录"
>
<mkdir
dir=
"${classes}"
/>
</target>
<target
name=
"compile"
depends=
"init"
description=
"编译src和gen两个源目录下的源文件到class目录下"
>
<javac
srcdir=
"${src}:${gen}"
destdir=
"${classes}"
>
<classpath
refid=
"Referenced libs"
/>
<compilerarg
line=
"-encoding UTF-8 "
/>
</javac>
</target>
<target
name=
"build"
depends=
"compile"
description=
"只选择我们需要的class文件打成jar包"
>
<jar
jarfile=
"${jarFile}"
basedir=
"${classes}"
>
<include
name=
"com/netease/emmagee/performance/*.class"
/>
<!-- <include name="com/netease/emmagee/performance/PerformanceMonitor.class" />
<include name="com/netease/qa/emmagee/utils/CpuInfo.class" />
<include name="com/netease/qa/emmagee/utils/MemoryInfo.class" />
<include name="com/netease/qa/emmagee/utils/TrafficInfo.class" /> -->
</jar>
</target>
</project>
gen/com/netease/qa/emmagee/R.java
浏览文件 @
8fdb357f
/* AUTO-GENERATED FILE. DO NOT MODIFY.
*
* This class was automatically generated by the
* aapt tool from the resource data it found. It
* should not be modified by hand.
*/
package
com.netease.qa.emmagee
;
public
final
class
R
{
public
static
final
class
attr
{
}
public
static
final
class
drawable
{
public
static
final
int
button_bg
=
0x7f020000
;
public
static
final
int
close
=
0x7f020001
;
public
static
final
int
ic_launcher
=
0x7f020002
;
public
static
final
int
icon
=
0x7f020003
;
public
static
final
int
meminfo
=
0x7f020004
;
}
public
static
final
class
id
{
public
static
final
int
Lin
=
0x7f050004
;
public
static
final
int
b
=
0x7f050001
;
public
static
final
int
cpu1
=
0x7f050013
;
public
static
final
int
cpu2
=
0x7f050014
;
public
static
final
int
cpu_info
=
0x7f050000
;
public
static
final
int
floating
=
0x7f050011
;
public
static
final
int
floating_Window
=
0x7f050010
;
public
static
final
int
image
=
0x7f05000a
;
public
static
final
int
img1
=
0x7f050002
;
public
static
final
int
img2
=
0x7f050003
;
public
static
final
int
memoryinfo
=
0x7f05000e
;
public
static
final
int
memtotal
=
0x7f050006
;
public
static
final
int
memunused
=
0x7f050005
;
public
static
final
int
processList
=
0x7f05000c
;
public
static
final
int
rb
=
0x7f050009
;
public
static
final
int
save
=
0x7f050012
;
public
static
final
int
test
=
0x7f05000d
;
public
static
final
int
text
=
0x7f05000b
;
public
static
final
int
time
=
0x7f05000f
;
public
static
final
int
traffic
=
0x7f050007
;
public
static
final
int
wifi
=
0x7f050008
;
}
public
static
final
class
layout
{
public
static
final
int
cpu
=
0x7f030000
;
public
static
final
int
floating
=
0x7f030001
;
public
static
final
int
list_item
=
0x7f030002
;
public
static
final
int
mainpage
=
0x7f030003
;
public
static
final
int
memory
=
0x7f030004
;
public
static
final
int
settings
=
0x7f030005
;
public
static
final
int
systemstat
=
0x7f030006
;
}
public
static
final
class
string
{
public
static
final
int
app_name
=
0x7f040001
;
public
static
final
int
app_name1
=
0x7f040002
;
public
static
final
int
app_name2
=
0x7f040003
;
public
static
final
int
app_name3
=
0x7f040004
;
public
static
final
int
app_name4
=
0x7f040005
;
public
static
final
int
bg
=
0x7f040009
;
public
static
final
int
closewifi
=
0x7f040011
;
public
static
final
int
collect
=
0x7f04000c
;
public
static
final
int
cpu
=
0x7f040008
;
public
static
final
int
hello
=
0x7f040000
;
public
static
final
int
memoryinfo
=
0x7f04000b
;
public
static
final
int
ok
=
0x7f04000f
;
public
static
final
int
openwifi
=
0x7f040010
;
public
static
final
int
seconds
=
0x7f04000d
;
public
static
final
int
start
=
0x7f040007
;
public
static
final
int
system
=
0x7f040006
;
public
static
final
int
testmemory
=
0x7f04000a
;
public
static
final
int
window
=
0x7f04000e
;
}
}
/* AUTO-GENERATED FILE. DO NOT MODIFY.
*
* This class was automatically generated by the
* aapt tool from the resource data it found. It
* should not be modified by hand.
*/
package
com.netease.qa.emmagee
;
public
final
class
R
{
public
static
final
class
attr
{
}
public
static
final
class
drawable
{
public
static
final
int
button_bg
=
0x7f020000
;
public
static
final
int
close
=
0x7f020001
;
public
static
final
int
ic_launcher
=
0x7f020002
;
public
static
final
int
icon
=
0x7f020003
;
public
static
final
int
meminfo
=
0x7f020004
;
}
public
static
final
class
id
{
public
static
final
int
Lin
=
0x7f050004
;
public
static
final
int
b
=
0x7f050001
;
public
static
final
int
batt
=
0x7f050007
;
public
static
final
int
cpu1
=
0x7f050018
;
public
static
final
int
cpu2
=
0x7f050019
;
public
static
final
int
cpu_info
=
0x7f050000
;
public
static
final
int
floating
=
0x7f050016
;
public
static
final
int
floating_Window
=
0x7f050015
;
public
static
final
int
image
=
0x7f05000b
;
public
static
final
int
img1
=
0x7f050002
;
public
static
final
int
img2
=
0x7f050003
;
public
static
final
int
memoryinfo
=
0x7f05000f
;
public
static
final
int
memtotal
=
0x7f050006
;
public
static
final
int
memunused
=
0x7f050005
;
public
static
final
int
password
=
0x7f050012
;
public
static
final
int
processList
=
0x7f05000d
;
public
static
final
int
rb
=
0x7f05000a
;
public
static
final
int
recipients
=
0x7f050014
;
public
static
final
int
save
=
0x7f050017
;
public
static
final
int
sender
=
0x7f050011
;
public
static
final
int
smtp
=
0x7f050013
;
public
static
final
int
test
=
0x7f05000e
;
public
static
final
int
text
=
0x7f05000c
;
public
static
final
int
time
=
0x7f050010
;
public
static
final
int
traffic
=
0x7f050008
;
public
static
final
int
wifi
=
0x7f050009
;
}
public
static
final
class
layout
{
public
static
final
int
cpu
=
0x7f030000
;
public
static
final
int
floating
=
0x7f030001
;
public
static
final
int
list_item
=
0x7f030002
;
public
static
final
int
mainpage
=
0x7f030003
;
public
static
final
int
memory
=
0x7f030004
;
public
static
final
int
settings
=
0x7f030005
;
public
static
final
int
systemstat
=
0x7f030006
;
}
public
static
final
class
string
{
public
static
final
int
app_name
=
0x7f040001
;
public
static
final
int
app_name1
=
0x7f040002
;
public
static
final
int
app_name2
=
0x7f040003
;
public
static
final
int
app_name3
=
0x7f040004
;
public
static
final
int
app_name4
=
0x7f040005
;
public
static
final
int
bg
=
0x7f040009
;
public
static
final
int
closewifi
=
0x7f040015
;
public
static
final
int
collect
=
0x7f04000c
;
public
static
final
int
cpu
=
0x7f040008
;
public
static
final
int
hello
=
0x7f040000
;
public
static
final
int
memoryinfo
=
0x7f04000b
;
public
static
final
int
ok
=
0x7f040013
;
public
static
final
int
openwifi
=
0x7f040014
;
public
static
final
int
password
=
0x7f04000f
;
public
static
final
int
receiver
=
0x7f04000d
;
public
static
final
int
seconds
=
0x7f040011
;
public
static
final
int
sender
=
0x7f04000e
;
public
static
final
int
smtp
=
0x7f040010
;
public
static
final
int
start
=
0x7f040007
;
public
static
final
int
system
=
0x7f040006
;
public
static
final
int
testmemory
=
0x7f04000a
;
public
static
final
int
window
=
0x7f040012
;
}
}
src/com/netease/emmagee/performance/CpuInfo.java
0 → 100644
浏览文件 @
8fdb357f
/*
* Copyright (c) 2012-2013 NetEase, Inc. and other contributors
*
* Licensed 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.
*
*/
package
com.netease.emmagee.performance
;
import
java.io.FileNotFoundException
;
import
java.io.IOException
;
import
java.io.RandomAccessFile
;
import
java.text.DecimalFormat
;
import
java.text.SimpleDateFormat
;
import
java.util.ArrayList
;
import
java.util.Calendar
;
import
android.content.Context
;
import
android.os.Build
;
import
android.util.Log
;
/**
* operate CPU information
* @author hz_liuxiao
*/
public
class
CpuInfo
{
private
static
final
String
LOG_TAG
=
"Emmagee-"
+
CpuInfo
.
class
.
getSimpleName
();
private
Context
context
;
private
long
processCpu
;
private
long
idleCpu
;
private
long
totalCpu
;
private
boolean
isInitialStatics
=
true
;
private
SimpleDateFormat
formatterFile
;
private
MemoryInfo
mi
;
private
long
totalMemorySize
;
private
long
initialTraffic
;
private
long
lastestTraffic
;
private
long
traffic
;
private
TrafficInfo
trafficInfo
;
private
ArrayList
<
String
>
cpuUsedRatio
;
private
long
totalCpu2
;
private
long
processCpu2
;
private
long
idleCpu2
;
private
String
processCpuRatio
=
""
;
private
String
totalCpuRatio
=
""
;
// public CpuInfo(Context context, int pid, String uid) {
// this.pid = pid;
// this.context = context;
// trafficInfo = new NetworkInfo(uid);
// formatterFile = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// mi = new MemoryInfo();
// totalMemorySize = mi.getTotalMemory();
// cpuUsedRatio = new ArrayList<String>();
// }
/**
* read the status of CPU. 0:processCpu 1:idleCpu 2:totalCpu
*
* @throws FileNotFoundException
*/
public
long
[]
readCpuStat
(
int
pid
)
{
String
processPid
=
Integer
.
toString
(
pid
);
String
cpuStatPath
=
"/proc/"
+
processPid
+
"/stat"
;
try
{
// monitor cpu stat of certain process
RandomAccessFile
processCpuInfo
=
new
RandomAccessFile
(
cpuStatPath
,
"r"
);
String
line
=
""
;
StringBuffer
stringBuffer
=
new
StringBuffer
();
stringBuffer
.
setLength
(
0
);
while
((
line
=
processCpuInfo
.
readLine
())
!=
null
)
{
stringBuffer
.
append
(
line
+
"\n"
);
}
String
[]
tok
=
stringBuffer
.
toString
().
split
(
" "
);
processCpu
=
Long
.
parseLong
(
tok
[
13
])
+
Long
.
parseLong
(
tok
[
14
]);
processCpuInfo
.
close
();
}
catch
(
FileNotFoundException
e
)
{
Log
.
e
(
LOG_TAG
,
"FileNotFoundException: "
+
e
.
getMessage
());
e
.
printStackTrace
();
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
try
{
// monitor total and idle cpu stat of certain process
RandomAccessFile
cpuInfo
=
new
RandomAccessFile
(
"/proc/stat"
,
"r"
);
String
[]
toks
=
cpuInfo
.
readLine
().
split
(
"\\s+"
);
idleCpu
=
Long
.
parseLong
(
toks
[
4
]);
totalCpu
=
Long
.
parseLong
(
toks
[
1
])
+
Long
.
parseLong
(
toks
[
2
])
+
Long
.
parseLong
(
toks
[
3
])
+
Long
.
parseLong
(
toks
[
4
])
+
Long
.
parseLong
(
toks
[
6
])
+
Long
.
parseLong
(
toks
[
5
])
+
Long
.
parseLong
(
toks
[
7
]);
cpuInfo
.
close
();
}
catch
(
FileNotFoundException
e
)
{
e
.
printStackTrace
();
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
return
new
long
[]
{
processCpu
,
idleCpu
,
totalCpu
};
}
/**
* get CPU name.
*
* @return CPU name
*/
public
String
getCpuName
()
{
try
{
RandomAccessFile
cpuStat
=
new
RandomAccessFile
(
"/proc/cpuinfo"
,
"r"
);
String
[]
cpu
=
cpuStat
.
readLine
().
split
(
":"
);
// cpu信息的前一段是含有processor字符串,此处替换为不显示
cpuStat
.
close
();
return
cpu
[
1
];
}
catch
(
IOException
e
)
{
Log
.
e
(
LOG_TAG
,
"IOException: "
+
e
.
getMessage
());
}
return
""
;
}
// /**
// * reserve used ratio of process CPU and total CPU, meanwhile collect
// * network traffic.
// *
// * @return network traffic ,used ratio of process CPU and total CPU in
// * certain interval
// */
// public ArrayList<String> getCpuRatioInfo(String currentBatt, String temp,
// String voltage) {
//
// DecimalFormat fomart = new DecimalFormat();
// fomart.setMaximumFractionDigits(2);
// fomart.setMinimumFractionDigits(2);
//
// readCpuStat();
// cpuUsedRatio.clear();
//
// try {
// String mDateTime2;
// Calendar cal = Calendar.getInstance();
// if ((Build.MODEL.equals("sdk"))
// || (Build.MODEL.equals("google_sdk"))) {
// mDateTime2 = formatterFile.format(cal.getTime().getTime() + 8
// * 60 * 60 * 1000);
// } else
// mDateTime2 = formatterFile.format(cal.getTime().getTime());
//
// if (isInitialStatics) {
// initialTraffic = trafficInfo.getTrafficInfo();
// isInitialStatics = false;
// } else {
// lastestTraffic = trafficInfo.getTrafficInfo();
// if (initialTraffic == -1)
// traffic = -1;
// else
// traffic = (lastestTraffic - initialTraffic + 1023) / 1024;
// processCpuRatio = fomart
// .format(100 * ((double) (processCpu - processCpu2) / ((double) (totalCpu
// - totalCpu2))));
// totalCpuRatio = fomart
// .format(100 * ((double) ((totalCpu - idleCpu) - (totalCpu2 - idleCpu2)) /
// (double) (totalCpu - totalCpu2)));
// long pidMemory = mi.getPidMemorySize(pid, context);
// String pMemory = fomart.format((double) pidMemory / 1024);
// long freeMemory = mi.getFreeMemorySize(context);
// String fMemory = fomart.format((double) freeMemory / 1024);
// String percent = "统计出错";
// if (totalMemorySize != 0) {
// percent = fomart
// .format(((double) pidMemory / (double) totalMemorySize) * 100);
// }
//
// // whether certain device supports traffic statics or not
// if (traffic == -1) {
// EmmageeService.bw.write(mDateTime2 + "," + pMemory + ","
// + percent + "," + fMemory + "," + processCpuRatio
// + "," + totalCpuRatio + "," + "本程序或本设备不支持流量统计"
// + "," + currentBatt + "," + temp + "," + voltage
// + "\r\n");
// } else {
// EmmageeService.bw
// .write(mDateTime2 + "," + pMemory + "," + percent
// + "," + fMemory + "," + processCpuRatio
// + "," + totalCpuRatio + "," + traffic + ","
// + currentBatt + "," + temp + "," + voltage
// + "\r\n");
// }
// }
// totalCpu2 = totalCpu;
// processCpu2 = processCpu;
// idleCpu2 = idleCpu;
// cpuUsedRatio.add(processCpuRatio);
// cpuUsedRatio.add(totalCpuRatio);
// cpuUsedRatio.add(String.valueOf(traffic));
// } catch (IOException e) {
// e.printStackTrace();
// // PttService.closeOpenedStream()
// }
// return cpuUsedRatio;
//
// }
}
src/com/netease/emmagee/performance/MemoryInfo.java
0 → 100644
浏览文件 @
8fdb357f
/*
* Copyright (c) 2012-2013 NetEase, Inc. and other contributors
*
* Licensed 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.
*
*/
package
com.netease.emmagee.performance
;
import
java.io.BufferedReader
;
import
java.io.FileReader
;
import
java.io.IOException
;
import
android.app.ActivityManager
;
import
android.content.Context
;
import
android.os.Debug
;
import
android.util.Log
;
/**
* operate memory information
* @author hz_liuxiao
*/
public
class
MemoryInfo
{
private
static
final
String
LOG_TAG
=
"Grape-"
+
MemoryInfo
.
class
.
getSimpleName
();
// private int pid;
// private Context context;
//
// public void MemoryInfo(int pid , Context context){
// this.pid = pid;
// this.context = context;
// }
/**
* get total memory of certain device.
*
* @return total memory of device
*/
public
long
getTotalMemory
()
{
String
memInfoPath
=
"/proc/meminfo"
;
String
readTemp
=
""
;
String
memTotal
=
""
;
long
memory
=
0
;
try
{
FileReader
fr
=
new
FileReader
(
memInfoPath
);
BufferedReader
localBufferedReader
=
new
BufferedReader
(
fr
,
8192
);
while
((
readTemp
=
localBufferedReader
.
readLine
())
!=
null
)
{
if
(
readTemp
.
contains
(
"MemTotal"
))
{
String
[]
total
=
readTemp
.
split
(
":"
);
memTotal
=
total
[
1
].
trim
();
}
}
localBufferedReader
.
close
();
String
[]
memKb
=
memTotal
.
split
(
" "
);
memTotal
=
memKb
[
0
].
trim
();
Log
.
d
(
LOG_TAG
,
"memTotal: "
+
memTotal
);
memory
=
Long
.
parseLong
(
memTotal
);
}
catch
(
IOException
e
)
{
Log
.
e
(
LOG_TAG
,
"IOException: "
+
e
.
getMessage
());
}
return
memory
;
}
/**
* get free memory.
*
* @return free memory of device
*
*/
public
long
getFreeMemorySize
(
Context
context
)
{
ActivityManager
.
MemoryInfo
outInfo
=
new
ActivityManager
.
MemoryInfo
();
ActivityManager
am
=
(
ActivityManager
)
context
.
getSystemService
(
Context
.
ACTIVITY_SERVICE
);
am
.
getMemoryInfo
(
outInfo
);
long
avaliMem
=
outInfo
.
availMem
;
return
avaliMem
/
1024
;
}
/**
* get the memory of process with certain pid.
*
* @param pid
* pid of process
* @param context
* context of certain activity
* @return memory usage of certain process
*/
public
int
getPidMemorySize
(
int
pid
,
Context
context
)
{
ActivityManager
am
=
(
ActivityManager
)
context
.
getSystemService
(
Context
.
ACTIVITY_SERVICE
);
int
[]
myMempid
=
new
int
[]
{
pid
};
Debug
.
MemoryInfo
[]
memoryInfo
=
am
.
getProcessMemoryInfo
(
myMempid
);
memoryInfo
[
0
].
getTotalSharedDirty
();
// int memSize = memoryInfo[0].dalvikPrivateDirty;
// TODO PSS
int
memSize
=
memoryInfo
[
0
].
getTotalPss
();
// int memSize = memoryInfo[0].getTotalPrivateDirty();
return
memSize
;
}
/**
* get the sdk version of phone.
*
* @return sdk version
*/
public
String
getSDKVersion
()
{
return
android
.
os
.
Build
.
VERSION
.
RELEASE
;
}
/**
* get phone type.
*
* @return phone type
*/
public
String
getPhoneType
()
{
return
android
.
os
.
Build
.
MODEL
;
}
}
src/com/netease/emmagee/performance/PerformanceMonitor.java
0 → 100644
浏览文件 @
8fdb357f
/*
* Copyright (c) 2012-2013 NetEase, Inc. and other contributors
*
* Licensed 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.
*
*/
package
com.netease.emmagee.performance
;
import
java.io.BufferedWriter
;
import
java.io.File
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
import
java.io.OutputStreamWriter
;
import
java.text.DecimalFormat
;
import
java.text.SimpleDateFormat
;
import
java.util.List
;
import
android.app.ActivityManager
;
import
android.app.ActivityManager.RunningAppProcessInfo
;
import
android.content.BroadcastReceiver
;
import
android.content.Context
;
import
android.content.Intent
;
import
android.content.IntentFilter
;
import
android.os.BatteryManager
;
import
android.os.Build
;
import
android.os.Handler
;
import
android.util.Log
;
/**
* Service running in background
* @author hz_liuxiao
*/
public
class
PerformanceMonitor
{
private
final
static
String
LOG_TAG
=
"Emmagee-"
+
PerformanceMonitor
.
class
.
getSimpleName
();
private
int
delaytime
=
1000
;
private
Handler
handler
=
new
Handler
();
public
BufferedWriter
bw
;
public
FileOutputStream
out
;
public
OutputStreamWriter
osw
;
public
String
resultFilePath
;
private
int
pid
,
uid
;
private
CpuInfo
cpuInfo
;
private
MemoryInfo
memoryInfo
;
private
TrafficInfo
networkInfo
;
private
SimpleDateFormat
formatterTime
;
private
DecimalFormat
fomart
;
private
boolean
isInitialStatic
=
true
;
private
long
processCpu1
,
processCpu2
,
totalCpu1
,
totalCpu2
,
idleCpu1
,
idleCpu2
;
private
long
startTraff
,
endTraff
,
intervalTraff
;
private
String
currentBatt
,
temperature
,
voltage
;
private
boolean
isRunnableStop
=
false
;
private
BatteryInfoBroadcastReceiver
receiver
;
private
Context
context
;
private
String
toolName
;
// private OrangeSolo orange;
public
PerformanceMonitor
(
Context
context
,
String
packageName
,
String
toolName
,
String
mDateTime
)
{
this
.
context
=
context
;
fomart
=
new
DecimalFormat
();
fomart
.
setMaximumFractionDigits
(
2
);
fomart
.
setMinimumFractionDigits
(
2
);
// 注册广播监听电量
receiver
=
new
BatteryInfoBroadcastReceiver
();
IntentFilter
filter
=
new
IntentFilter
(
Intent
.
ACTION_BATTERY_CHANGED
);
context
.
registerReceiver
(
receiver
,
filter
);
// formatterTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// ObjectSerializable serializable = (ObjectSerializable)
// intent.getSerializableExtra("orange");
// orange = serializable.getOrange();
getAppInfo
(
packageName
);
// 不在初始化的时候创建报告,而是在真正做记录的时候创建
// creatReport(toolName, mDateTime);
this
.
toolName
=
toolName
;
cpuInfo
=
new
CpuInfo
();
memoryInfo
=
new
MemoryInfo
();
networkInfo
=
new
TrafficInfo
();
}
/**
* get pid and uid
*
* @param packageName
*/
private
void
getAppInfo
(
String
packageName
)
{
ActivityManager
am
=
(
ActivityManager
)
context
.
getSystemService
(
Context
.
ACTIVITY_SERVICE
);
List
<
RunningAppProcessInfo
>
run
=
am
.
getRunningAppProcesses
();
// PackageManager pm = context.getPackageManager();
for
(
RunningAppProcessInfo
runningProcess
:
run
)
{
if
(
packageName
.
equals
(
runningProcess
.
processName
))
{
uid
=
runningProcess
.
uid
;
pid
=
runningProcess
.
pid
;
Log
.
d
(
LOG_TAG
,
"pid = "
+
pid
);
Log
.
d
(
LOG_TAG
,
"uid = "
+
uid
);
break
;
}
}
}
/**
* write the test result to csv format report.
*/
private
void
creatReport
(
String
toolName
,
String
dateTime
)
{
Log
.
d
(
LOG_TAG
,
"start write report"
);
// Calendar cal = Calendar.getInstance();
// SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
// String mDateTime;
// if ((Build.MODEL.equals("sdk")) || (Build.MODEL.equals("google_sdk")))
// mDateTime = formatter.format(cal.getTime().getTime() + 8 * 60 * 60
// * 1000);
// else
// mDateTime = formatter.format(cal.getTime().getTime());
String
dir
=
""
;
if
(
android
.
os
.
Environment
.
getExternalStorageState
().
equals
(
android
.
os
.
Environment
.
MEDIA_MOUNTED
))
{
dir
=
android
.
os
.
Environment
.
getExternalStorageDirectory
()
+
File
.
separator
+
toolName
;
}
else
{
dir
=
context
.
getFilesDir
().
getPath
()
+
File
.
separator
+
toolName
;
}
// resultFilePath = dir + File.separator + toolName + "-" + mDateTime + ".csv";
resultFilePath
=
dir
+
File
.
separator
+
toolName
+
"-"
+
Build
.
VERSION
.
SDK_INT
+
"-"
+
Build
.
MODEL
.
replace
(
" "
,
"-"
)
+
"-PerformanceMonitor"
+
".csv"
;
try
{
// 创建目录
File
fileDir
=
new
File
(
dir
);
if
(!
fileDir
.
exists
())
{
fileDir
.
mkdirs
();
}
// 创建文件
File
resultFile
=
new
File
(
resultFilePath
);
// 只有在性能结果文件不存在的情况下才创建文件,并生成头文件,让文件只保持一份就好
if
(!
resultFile
.
exists
())
{
resultFile
.
createNewFile
();
out
=
new
FileOutputStream
(
resultFile
,
true
);
// 在文件内容后继续加内容
osw
=
new
OutputStreamWriter
(
out
,
"GBK"
);
bw
=
new
BufferedWriter
(
osw
);
// 生成头文件
bw
.
write
(
"测试用例信息"
+
","
+
"时间"
+
","
+
"应用占用内存PSS(MB)"
+
","
+
"应用占用内存比(%)"
+
","
+
" 机器剩余内存(MB)"
+
","
+
"应用占用CPU率(%)"
+
","
+
"CPU总使用率(%)"
+
","
+
"流量(KB)"
+
","
+
"当前电量"
+
","
+
"电池温度(C)"
+
","
+
"电压(V)"
+
"\r\n"
);
bw
.
flush
();
}
else
{
out
=
new
FileOutputStream
(
resultFile
,
true
);
// 在文件内容后继续加内容
osw
=
new
OutputStreamWriter
(
out
,
"GBK"
);
bw
=
new
BufferedWriter
(
osw
);
}
}
catch
(
IOException
e
)
{
Log
.
e
(
LOG_TAG
,
e
.
getMessage
());
}
Log
.
d
(
LOG_TAG
,
"end write report"
);
}
/**
* write data into certain file
*/
public
void
writePerformanceData
(
String
mDateTime
)
{
if
(
isInitialStatic
)
{
// 创建相应的性能数据报告
creatReport
(
toolName
,
mDateTime
);
startTraff
=
networkInfo
.
getTrafficInfo
(
uid
);
isInitialStatic
=
false
;
}
// Network
endTraff
=
networkInfo
.
getTrafficInfo
(
uid
);
if
(
startTraff
==
-
1
)
intervalTraff
=
-
1
;
else
intervalTraff
=
(
endTraff
-
startTraff
+
1023
)
/
1024
;
// CPU
processCpu1
=
cpuInfo
.
readCpuStat
(
pid
)[
0
];
idleCpu1
=
cpuInfo
.
readCpuStat
(
pid
)[
1
];
totalCpu1
=
cpuInfo
.
readCpuStat
(
pid
)[
2
];
String
processCpuRatio
=
fomart
.
format
(
100
*
((
double
)
(
processCpu1
-
processCpu2
)
/
((
double
)
(
totalCpu1
-
totalCpu2
))));
String
totalCpuRatio
=
fomart
.
format
(
100
*
((
double
)
((
totalCpu1
-
idleCpu1
)
-
(
totalCpu2
-
idleCpu2
))
/
(
double
)
(
totalCpu1
-
totalCpu2
)));
// Memory
long
pidMemory
=
memoryInfo
.
getPidMemorySize
(
pid
,
context
);
String
pss
=
fomart
.
format
((
double
)
pidMemory
/
1024
);
long
freeMemory
=
memoryInfo
.
getFreeMemorySize
(
context
);
String
freeMem
=
fomart
.
format
((
double
)
freeMemory
/
1024
);
long
totalMemorySize
=
memoryInfo
.
getTotalMemory
();
String
percent
=
"统计出错"
;
if
(
totalMemorySize
!=
0
)
{
percent
=
fomart
.
format
(((
double
)
pidMemory
/
(
double
)
totalMemorySize
)
*
100
);
}
try
{
if
(
intervalTraff
==
-
1
)
{
bw
.
write
(
this
.
getTestCaseInfo
()
+
"-"
+
this
.
getActionInfo
()
+
","
+
mDateTime
+
","
+
pss
+
","
+
percent
+
","
+
freeMem
+
","
+
processCpuRatio
+
","
+
totalCpuRatio
+
","
+
"本程序或本设备不支持流量统计"
+
","
+
currentBatt
+
","
+
temperature
+
","
+
voltage
+
"\r\n"
);
}
else
{
bw
.
write
(
this
.
getTestCaseInfo
()
+
"-"
+
this
.
getActionInfo
()
+
","
+
mDateTime
+
","
+
pss
+
","
+
percent
+
","
+
freeMem
+
","
+
processCpuRatio
+
","
+
totalCpuRatio
+
","
+
intervalTraff
+
","
+
currentBatt
+
","
+
temperature
+
","
+
voltage
+
"\r\n"
);
}
bw
.
flush
();
Log
.
i
(
LOG_TAG
,
"*** writePerformanceData on "
+
mDateTime
+
" *** "
);
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
processCpu2
=
processCpu1
;
idleCpu2
=
idleCpu1
;
totalCpu2
=
totalCpu1
;
}
private
Runnable
task
=
new
Runnable
()
{
public
void
run
()
{
if
(!
isRunnableStop
)
{
handler
.
postDelayed
(
this
,
delaytime
);
// writePerformanceData();
}
};
};
/**
* 电量广播类
* @author hz_liuxiao@corp.netease.com
*
*/
public
class
BatteryInfoBroadcastReceiver
extends
BroadcastReceiver
{
@Override
public
void
onReceive
(
Context
context
,
Intent
intent
)
{
if
(
Intent
.
ACTION_BATTERY_CHANGED
.
equals
(
intent
.
getAction
()))
{
int
level
=
intent
.
getIntExtra
(
BatteryManager
.
EXTRA_LEVEL
,
0
);
int
scale
=
intent
.
getIntExtra
(
BatteryManager
.
EXTRA_SCALE
,
-
1
);
currentBatt
=
String
.
valueOf
(
level
*
100
/
scale
)
+
"%"
;
voltage
=
String
.
valueOf
(
intent
.
getIntExtra
(
BatteryManager
.
EXTRA_VOLTAGE
,
-
1
)
*
1.0
/
1000
);
temperature
=
String
.
valueOf
(
intent
.
getIntExtra
(
BatteryManager
.
EXTRA_TEMPERATURE
,
-
1
)
*
1.0
/
10
);
int
status
=
intent
.
getIntExtra
(
BatteryManager
.
EXTRA_STATUS
,
-
1
);
}
}
}
/**
* close all opened stream.
*/
public
void
closeOpenedStream
()
{
try
{
if
(
bw
!=
null
)
bw
.
close
();
if
(
osw
!=
null
)
osw
.
close
();
if
(
out
!=
null
)
out
.
close
();
}
catch
(
Exception
e
)
{
Log
.
d
(
LOG_TAG
,
e
.
getMessage
());
}
}
public
void
onDestroy
()
{
context
.
unregisterReceiver
(
receiver
);
isRunnableStop
=
true
;
closeOpenedStream
();
}
/**
* 从栈中获取类名和测试方法名
*
* @return 类名+测试方法名
*/
private
String
getTestCaseInfo
()
{
String
testCaseInfo
=
""
;
String
testName
,
className
;
StackTraceElement
[]
stack
=
(
new
Throwable
()).
getStackTrace
();
int
i
=
0
;
while
(
i
<
stack
.
length
)
{
// 在setUp和testXXX中会有orange的操作方法
if
(
stack
[
i
].
getMethodName
().
toString
().
startsWith
(
"test"
)
||
stack
[
i
].
getMethodName
().
toString
().
startsWith
(
"setUp"
))
{
break
;
}
i
++;
}
if
(
i
>=
stack
.
length
)
{
testCaseInfo
=
"No TestCase Info"
;
}
else
{
// “.”在正则中代码任意字符,不能用来分割,如需使用则通过“//.”转义
String
[]
packageName
=
stack
[
i
].
getClassName
().
toString
().
split
(
"\\."
);
className
=
packageName
[
packageName
.
length
-
1
];
// className = stack[i].getClassName().toString();
testName
=
stack
[
i
].
getMethodName
().
toString
();
testCaseInfo
=
className
+
"."
+
testName
;
}
// Log.i(LOG_TAG, "*** getTestCaseInfo =" + testCaseInfo + " *** ");
return
testCaseInfo
;
}
/**
* 从栈中获取相应操作方法名,有clickXXX,enterXXX,scrollXXX,typeXXX
*
* @return 类名+测试方法名
*/
private
String
getActionInfo
()
{
String
actionInfo
=
""
;
String
testName
,
className
;
StackTraceElement
[]
stack
=
(
new
Throwable
()).
getStackTrace
();
int
i
=
0
;
while
(
i
<
stack
.
length
)
{
// 类对应的是OrangeSolo,
if
(
stack
[
i
].
getMethodName
().
toString
().
startsWith
(
"click"
)
||
stack
[
i
].
getMethodName
().
toString
().
startsWith
(
"enter"
)
||
stack
[
i
].
getMethodName
().
toString
().
startsWith
(
"scroll"
)
||
stack
[
i
].
getMethodName
().
toString
().
startsWith
(
"type"
))
{
break
;
}
i
++;
}
if
(
i
>=
stack
.
length
)
{
actionInfo
=
"No Action Info"
;
}
else
{
//className暂时不用展现
testName
=
stack
[
i
].
getMethodName
().
toString
();
actionInfo
=
testName
;
}
return
actionInfo
;
}
}
\ No newline at end of file
src/com/netease/emmagee/performance/TrafficInfo.java
0 → 100644
浏览文件 @
8fdb357f
/*
* Copyright (c) 2012-2013 NetEase, Inc. and other contributors
*
* Licensed 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.
*
*/
package
com.netease.emmagee.performance
;
import
java.io.FileNotFoundException
;
import
java.io.IOException
;
import
java.io.RandomAccessFile
;
import
android.util.Log
;
/**
* information of network traffic
* @author hz_liuxiao
*/
public
class
TrafficInfo
{
private
static
final
String
LOG_TAG
=
"Grape-"
+
TrafficInfo
.
class
.
getSimpleName
();
/**
* get total network traffic, which is the sum of upload and download
* traffic.
*
* @return total traffic include received and send traffic
*/
public
long
getTrafficInfo
(
int
uid
)
{
Log
.
i
(
LOG_TAG
,
"get traffic information"
);
RandomAccessFile
rafRcv
=
null
,
rafSnd
=
null
;
String
rcvPath
=
"/proc/uid_stat/"
+
uid
+
"/tcp_rcv"
;
String
sndPath
=
"/proc/uid_stat/"
+
uid
+
"/tcp_snd"
;
long
rcvTraffic
=
-
1
;
long
sndTraffic
=
-
1
;
try
{
rafRcv
=
new
RandomAccessFile
(
rcvPath
,
"r"
);
rafSnd
=
new
RandomAccessFile
(
sndPath
,
"r"
);
rcvTraffic
=
Long
.
parseLong
(
rafRcv
.
readLine
());
sndTraffic
=
Long
.
parseLong
(
rafSnd
.
readLine
());
}
catch
(
FileNotFoundException
e
)
{
rcvTraffic
=
-
1
;
sndTraffic
=
-
1
;
}
catch
(
NumberFormatException
e
)
{
Log
.
e
(
LOG_TAG
,
"NumberFormatException: "
+
e
.
getMessage
());
e
.
printStackTrace
();
}
catch
(
IOException
e
)
{
Log
.
e
(
LOG_TAG
,
"IOException: "
+
e
.
getMessage
());
e
.
printStackTrace
();
}
finally
{
try
{
if
(
rafRcv
!=
null
)
{
rafRcv
.
close
();
}
if
(
rafSnd
!=
null
)
rafSnd
.
close
();
}
catch
(
IOException
e
)
{
Log
.
i
(
LOG_TAG
,
"close randomAccessFile exception: "
+
e
.
getMessage
());
}
}
if
(
rcvTraffic
==
-
1
||
sndTraffic
==
-
1
)
{
return
-
1
;
}
else
return
rcvTraffic
+
sndTraffic
;
}
}
src/com/netease/qa/emmagee/utils/CpuInfo.java
浏览文件 @
8fdb357f
...
...
@@ -24,12 +24,12 @@ import java.text.SimpleDateFormat;
import
java.util.ArrayList
;
import
java.util.Calendar
;
import
com.netease.qa.emmagee.service.EmmageeService
;
import
android.content.Context
;
import
android.os.Build
;
import
android.util.Log
;
import
com.netease.qa.emmagee.service.EmmageeService
;
/**
* operate CPU information
*
...
...
@@ -60,6 +60,10 @@ public class CpuInfo {
private
String
totalCpuRatio
=
""
;
private
int
pid
;
public
CpuInfo
()
{
}
public
CpuInfo
(
Context
context
,
int
pid
,
String
uid
)
{
this
.
pid
=
pid
;
this
.
context
=
context
;
...
...
@@ -115,6 +119,52 @@ public class CpuInfo {
}
}
/**
* read the status of CPU. 0:processCpu 1:idleCpu 2:totalCpu
*
* @throws FileNotFoundException
*/
public
long
[]
readCpuStat
(
int
pid
)
{
String
processPid
=
Integer
.
toString
(
pid
);
String
cpuStatPath
=
"/proc/"
+
processPid
+
"/stat"
;
try
{
// monitor cpu stat of certain process
RandomAccessFile
processCpuInfo
=
new
RandomAccessFile
(
cpuStatPath
,
"r"
);
String
line
=
""
;
StringBuffer
stringBuffer
=
new
StringBuffer
();
stringBuffer
.
setLength
(
0
);
while
((
line
=
processCpuInfo
.
readLine
())
!=
null
)
{
stringBuffer
.
append
(
line
+
"\n"
);
}
String
[]
tok
=
stringBuffer
.
toString
().
split
(
" "
);
processCpu
=
Long
.
parseLong
(
tok
[
13
])
+
Long
.
parseLong
(
tok
[
14
]);
processCpuInfo
.
close
();
}
catch
(
FileNotFoundException
e
)
{
Log
.
e
(
LOG_TAG
,
"FileNotFoundException: "
+
e
.
getMessage
());
e
.
printStackTrace
();
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
try
{
// monitor total and idle cpu stat of certain process
RandomAccessFile
cpuInfo
=
new
RandomAccessFile
(
"/proc/stat"
,
"r"
);
String
[]
toks
=
cpuInfo
.
readLine
().
split
(
"\\s+"
);
idleCpu
=
Long
.
parseLong
(
toks
[
4
]);
totalCpu
=
Long
.
parseLong
(
toks
[
1
])
+
Long
.
parseLong
(
toks
[
2
])
+
Long
.
parseLong
(
toks
[
3
])
+
Long
.
parseLong
(
toks
[
4
])
+
Long
.
parseLong
(
toks
[
6
])
+
Long
.
parseLong
(
toks
[
5
])
+
Long
.
parseLong
(
toks
[
7
]);
cpuInfo
.
close
();
}
catch
(
FileNotFoundException
e
)
{
e
.
printStackTrace
();
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
return
new
long
[]
{
processCpu
,
idleCpu
,
totalCpu
};
}
/**
* get CPU name.
*
...
...
src/com/netease/qa/emmagee/utils/TrafficInfo.java
浏览文件 @
8fdb357f
...
...
@@ -38,6 +38,9 @@ public class TrafficInfo {
this
.
uid
=
uid
;
}
public
TrafficInfo
()
{
}
/**
* get total network traffic, which is the sum of upload and download
* traffic.
...
...
@@ -82,4 +85,49 @@ public class TrafficInfo {
}
else
return
rcvTraffic
+
sndTraffic
;
}
/**
* get total network traffic, which is the sum of upload and download
* traffic.
*
* @return total traffic include received and send traffic
*/
public
long
getTrafficInfo
(
int
uid
)
{
Log
.
i
(
LOG_TAG
,
"get traffic information"
);
RandomAccessFile
rafRcv
=
null
,
rafSnd
=
null
;
String
rcvPath
=
"/proc/uid_stat/"
+
uid
+
"/tcp_rcv"
;
String
sndPath
=
"/proc/uid_stat/"
+
uid
+
"/tcp_snd"
;
long
rcvTraffic
=
-
1
;
long
sndTraffic
=
-
1
;
try
{
rafRcv
=
new
RandomAccessFile
(
rcvPath
,
"r"
);
rafSnd
=
new
RandomAccessFile
(
sndPath
,
"r"
);
rcvTraffic
=
Long
.
parseLong
(
rafRcv
.
readLine
());
sndTraffic
=
Long
.
parseLong
(
rafSnd
.
readLine
());
}
catch
(
FileNotFoundException
e
)
{
rcvTraffic
=
-
1
;
sndTraffic
=
-
1
;
}
catch
(
NumberFormatException
e
)
{
Log
.
e
(
LOG_TAG
,
"NumberFormatException: "
+
e
.
getMessage
());
e
.
printStackTrace
();
}
catch
(
IOException
e
)
{
Log
.
e
(
LOG_TAG
,
"IOException: "
+
e
.
getMessage
());
e
.
printStackTrace
();
}
finally
{
try
{
if
(
rafRcv
!=
null
)
{
rafRcv
.
close
();
}
if
(
rafSnd
!=
null
)
rafSnd
.
close
();
}
catch
(
IOException
e
)
{
Log
.
i
(
LOG_TAG
,
"close randomAccessFile exception: "
+
e
.
getMessage
());
}
}
if
(
rcvTraffic
==
-
1
||
sndTraffic
==
-
1
)
{
return
-
1
;
}
else
return
rcvTraffic
+
sndTraffic
;
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录