Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
蜕变的菜鸟
glances
提交
fafcd176
G
glances
项目概览
蜕变的菜鸟
/
glances
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
G
glances
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
fafcd176
编写于
11月 17, 2014
作者:
N
Nicolas Hennion
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #444 from desbma/process-tree
上级
b8b6f422
b7086932
变更
7
展开全部
隐藏空白更改
内联
并排
Showing
7 changed file
with
589 addition
and
253 deletion
+589
-253
AUTHORS
AUTHORS
+3
-0
glances/core/glances_main.py
glances/core/glances_main.py
+2
-0
glances/core/glances_processes.py
glances/core/glances_processes.py
+254
-36
glances/core/glances_standalone.py
glances/core/glances_standalone.py
+5
-1
glances/outputs/glances_curses.py
glances/outputs/glances_curses.py
+9
-2
glances/plugins/glances_processcount.py
glances/plugins/glances_processcount.py
+1
-0
glances/plugins/glances_processlist.py
glances/plugins/glances_processlist.py
+315
-214
未找到文件。
AUTHORS
浏览文件 @
fafcd176
...
...
@@ -16,6 +16,9 @@ http://ifup.org/
Jon Renner (aka) Jrenner
https://github.com/jrenner
Maxime Desbrus (aka) desbma
https://github.com/desbma
=========
Packagers
=========
...
...
glances/core/glances_main.py
浏览文件 @
fafcd176
...
...
@@ -132,6 +132,8 @@ class GlancesMain(object):
if
not
is_windows
:
parser
.
add_argument
(
'--hide-kernel-threads'
,
action
=
'store_true'
,
default
=
False
,
dest
=
'no_kernel_threads'
,
help
=
_
(
'hide kernel threads in process list'
))
parser
.
add_argument
(
'--tree'
,
action
=
'store_true'
,
default
=
False
,
dest
=
'process_tree'
,
help
=
_
(
'display processes as a tree'
))
parser
.
add_argument
(
'-b'
,
'--byte'
,
action
=
'store_true'
,
default
=
False
,
dest
=
'byte'
,
help
=
_
(
'display network rate in byte per second'
))
parser
.
add_argument
(
'-1'
,
'--percpu'
,
action
=
'store_true'
,
default
=
False
,
...
...
glances/core/glances_processes.py
100644 → 100755
浏览文件 @
fafcd176
...
...
@@ -22,10 +22,188 @@ from glances.core.glances_globals import is_linux, is_bsd, is_mac, is_windows, l
from
glances.core.glances_timer
import
Timer
,
getTimeSinceLastUpdate
# Import Python lib
import
collections
import
psutil
import
re
class
ProcessTreeNode
(
object
):
"""
Represent a process tree.
We avoid recursive algorithm to manipulate the tree because function calls are expensive with CPython.
"""
def
__init__
(
self
,
process
=
None
,
stats
=
None
,
sort_key
=
None
,
root
=
False
):
self
.
process
=
process
self
.
stats
=
stats
self
.
children
=
[]
self
.
children_sorted
=
False
self
.
sort_key
=
sort_key
self
.
reverse_sorting
=
(
self
.
sort_key
!=
"name"
)
self
.
is_root
=
root
def
__str__
(
self
):
""" Return the tree as a string for debugging. """
lines
=
[]
nodes_to_print
=
collections
.
deque
([
collections
.
deque
([(
"#"
,
self
)])])
while
nodes_to_print
:
indent_str
,
current_node
=
nodes_to_print
[
-
1
].
pop
()
if
not
nodes_to_print
[
-
1
]:
nodes_to_print
.
pop
()
if
current_node
.
is_root
:
lines
.
append
(
indent_str
)
else
:
lines
.
append
(
"%s[%s]"
%
(
indent_str
,
current_node
.
process
.
name
()))
indent_str
=
" "
*
(
len
(
lines
[
-
1
])
-
1
)
children_nodes_to_print
=
collections
.
deque
()
for
child
in
current_node
.
children
:
if
child
is
current_node
.
children
[
-
1
]:
tree_char
=
"└─"
else
:
tree_char
=
"├─"
children_nodes_to_print
.
appendleft
((
indent_str
+
tree_char
,
child
))
if
children_nodes_to_print
:
nodes_to_print
.
append
(
children_nodes_to_print
)
return
"
\n
"
.
join
(
lines
)
def
setSorting
(
self
,
key
,
reverse
):
""" Set sorting key or func for user with __iter__ (affects the whole tree from this node). """
if
(
self
.
sort_key
!=
key
)
or
(
self
.
reverse_sorting
!=
reverse
):
nodes_to_flag_unsorted
=
collections
.
deque
([
self
])
while
nodes_to_flag_unsorted
:
current_node
=
nodes_to_flag_unsorted
.
pop
()
current_node
.
children_sorted
=
False
current_node
.
sort_key
=
key
current_node
.
reverse_sorting
=
reverse
nodes_to_flag_unsorted
.
extend
(
current_node
.
children
)
def
getWeight
(
self
):
""" Return "weight" of a process and all its children for sorting. """
if
self
.
sort_key
==
"name"
:
return
self
.
stats
[
self
.
sort_key
]
# sum ressource usage for self and children
total
=
0
nodes_to_sum
=
collections
.
deque
([
self
])
while
nodes_to_sum
:
current_node
=
nodes_to_sum
.
pop
()
if
callable
(
self
.
sort_key
):
total
+=
self
.
sort_key
(
current_node
.
stats
)
elif
self
.
sort_key
==
"io_counters"
:
stats
=
current_node
.
stats
[
self
.
sort_key
]
total
+=
stats
[
0
]
-
stats
[
2
]
+
stats
[
1
]
-
stats
[
3
]
else
:
total
+=
current_node
.
stats
[
self
.
sort_key
]
nodes_to_sum
.
extend
(
current_node
.
children
)
return
total
def
__len__
(
self
):
"""Return the number of nodes in the tree."""
total
=
0
nodes_to_sum
=
collections
.
deque
([
self
])
while
nodes_to_sum
:
current_node
=
nodes_to_sum
.
pop
()
if
not
current_node
.
is_root
:
total
+=
1
nodes_to_sum
.
extend
(
current_node
.
children
)
return
total
def
__iter__
(
self
):
""" Iterator returning ProcessTreeNode in sorted order, recursively. """
if
not
self
.
is_root
:
yield
self
if
not
self
.
children_sorted
:
# optimization to avoid sorting twice (once when limiting the maximum processes to grab stats for,
# and once before displaying)
self
.
children
.
sort
(
key
=
self
.
__class__
.
getWeight
,
reverse
=
self
.
reverse_sorting
)
self
.
children_sorted
=
True
for
child
in
self
.
children
:
for
n
in
iter
(
child
):
yield
n
def
iterChildren
(
self
,
exclude_incomplete_stats
=
True
):
"""
Iterator returning ProcessTreeNode in sorted order (only children of this node, non recursive).
If exclude_incomplete_stats is True, exclude processes not having full statistics.
It can happen after a resort (change of sort key) because process stats are not grabbed immediately,
but only at next full update.
"""
if
not
self
.
children_sorted
:
# optimization to avoid sorting twice (once when limiting the maximum processes to grab stats for,
# and once before displaying)
self
.
children
.
sort
(
key
=
self
.
__class__
.
getWeight
,
reverse
=
self
.
reverse_sorting
)
self
.
children_sorted
=
True
for
child
in
self
.
children
:
if
(
not
exclude_incomplete_stats
)
or
(
"time_since_update"
in
child
.
stats
):
yield
child
def
findProcess
(
self
,
process
):
""" Search in tree for the ProcessTreeNode owning process, return it or None if not found. """
nodes_to_search
=
collections
.
deque
([
self
])
while
nodes_to_search
:
current_node
=
nodes_to_search
.
pop
()
if
(
not
current_node
.
is_root
)
and
(
current_node
.
process
.
pid
==
process
.
pid
):
return
current_node
nodes_to_search
.
extend
(
current_node
.
children
)
@
staticmethod
def
buildTree
(
process_dict
,
sort_key
,
hide_kernel_threads
):
""" Build a process tree using using parent/child relationships, and return the tree root node. """
tree_root
=
ProcessTreeNode
(
root
=
True
)
nodes_to_add_last
=
collections
.
deque
()
# first pass: add nodes whose parent are in the tree
for
process
,
stats
in
process_dict
.
items
():
new_node
=
ProcessTreeNode
(
process
,
stats
,
sort_key
)
try
:
parent_process
=
process
.
parent
()
except
psutil
.
NoSuchProcess
:
# parent is dead, consider no parent
parent_process
=
None
if
parent_process
is
None
:
# no parent, add this node at the top level
tree_root
.
children
.
append
(
new_node
)
elif
hide_kernel_threads
and
(
not
is_windows
)
and
(
parent_process
.
gids
().
real
==
0
):
# parent is a kernel thread, add this node at the top level
tree_root
.
children
.
append
(
new_node
)
else
:
parent_node
=
tree_root
.
findProcess
(
parent_process
)
if
parent_node
is
not
None
:
# parent is already in the tree, add a new child
parent_node
.
children
.
append
(
new_node
)
else
:
# parent is not in tree, add this node later
nodes_to_add_last
.
append
(
new_node
)
# next pass(es): add nodes to their parents if it could not be done in previous pass
while
nodes_to_add_last
:
node_to_add
=
nodes_to_add_last
.
popleft
()
# pop from left and append to right to avoid infinite loop
try
:
parent_process
=
node_to_add
.
process
.
parent
()
except
psutil
.
NoSuchProcess
:
# parent is dead, consider no parent, add this node at the top level
tree_root
.
children
.
append
(
node_to_add
)
else
:
if
parent_process
is
None
:
# parent is None now, but was not at previous pass (can occur on Windows only)
# consider no parent, add this node at the top level
tree_root
.
children
.
append
(
node_to_add
)
else
:
parent_node
=
tree_root
.
findProcess
(
parent_process
)
if
parent_node
is
not
None
:
# parent is already in the tree, add a new child
parent_node
.
children
.
append
(
node_to_add
)
else
:
# parent is not in tree, add this node later
nodes_to_add_last
.
append
(
node_to_add
)
return
tree_root
class
GlancesProcesses
(
object
):
"""Get processed stats using the psutil library."""
...
...
@@ -46,6 +224,10 @@ class GlancesProcesses(object):
# value = [ read_bytes_old, write_bytes_old ]
self
.
io_old
=
{}
# Wether or not to enable process tree
self
.
_enable_tree
=
False
self
.
process_tree
=
None
# Init stats
self
.
resetsort
()
self
.
processlist
=
[]
...
...
@@ -135,6 +317,14 @@ class GlancesProcesses(object):
""" Ignore kernel threads in process list. """
self
.
no_kernel_threads
=
True
def
enable_tree
(
self
):
""" Enable process tree. """
self
.
_enable_tree
=
True
def
is_tree_enabled
(
self
):
""" Return True if process tree is enabled, False instead. """
return
self
.
_enable_tree
def
__get_process_stats
(
self
,
proc
,
mandatory_stats
=
True
,
standard_stats
=
True
,
...
...
@@ -383,44 +573,66 @@ class GlancesProcesses(object):
except
:
pass
# Process optimization
# Only retreive stats for visible processes (get_max_processes)
if
self
.
get_max_processes
()
is
not
None
:
# Sort the internal dict and cut the top N (Return a list of tuple)
# tuple=key (proc), dict (returned by __get_process_stats)
try
:
processiter
=
sorted
(
processdict
.
items
(),
key
=
lambda
x
:
x
[
1
][
self
.
getsortkey
()],
reverse
=
True
)
except
TypeError
:
# Fallback to all process (issue #423)
processloop
=
processdict
.
items
()
first
=
False
else
:
processloop
=
processiter
[
0
:
self
.
get_max_processes
()]
first
=
True
if
self
.
_enable_tree
:
self
.
process_tree
=
ProcessTreeNode
.
buildTree
(
processdict
,
self
.
getsortkey
(),
self
.
no_kernel_threads
)
for
i
,
node
in
enumerate
(
self
.
process_tree
):
# Only retreive stats for visible processes (get_max_processes)
if
(
self
.
get_max_processes
()
is
not
None
)
and
(
i
>=
self
.
get_max_processes
()):
break
# add standard stats
new_stats
=
self
.
__get_process_stats
(
node
.
process
,
mandatory_stats
=
False
,
standard_stats
=
True
,
extended_stats
=
False
)
if
new_stats
is
not
None
:
node
.
stats
.
update
(
new_stats
)
# Add a specific time_since_update stats for bitrate
node
.
stats
[
'time_since_update'
]
=
time_since_update
else
:
# Get all processes stats
processloop
=
processdict
.
items
()
first
=
False
for
i
in
processloop
:
# Already existing mandatory stats
procstat
=
i
[
1
]
# Process optimization
# Only retreive stats for visible processes (get_max_processes)
if
self
.
get_max_processes
()
is
not
None
:
# Update with standard stats
# and extended stats but only for TOP (first) process
s
=
self
.
__get_process_stats
(
i
[
0
],
mandatory_stats
=
False
,
standard_stats
=
True
,
extended_stats
=
first
)
if
s
is
None
:
continue
procstat
.
update
(
s
)
# Add a specific time_since_update stats for bitrate
procstat
[
'time_since_update'
]
=
time_since_update
# Update process list
self
.
processlist
.
append
(
procstat
)
# Next...
first
=
False
# Sort the internal dict and cut the top N (Return a list of tuple)
# tuple=key (proc), dict (returned by __get_process_stats)
try
:
processiter
=
sorted
(
processdict
.
items
(),
key
=
lambda
x
:
x
[
1
][
self
.
getsortkey
()],
reverse
=
True
)
except
TypeError
:
# Fallback to all process (issue #423)
processloop
=
processdict
.
items
()
first
=
False
else
:
processloop
=
processiter
[
0
:
self
.
get_max_processes
()]
first
=
True
else
:
# Get all processes stats
processloop
=
processdict
.
items
()
first
=
False
for
i
in
processloop
:
# Already existing mandatory stats
procstat
=
i
[
1
]
if
self
.
get_max_processes
()
is
not
None
:
# Update with standard stats
# and extended stats but only for TOP (first) process
s
=
self
.
__get_process_stats
(
i
[
0
],
mandatory_stats
=
False
,
standard_stats
=
True
,
extended_stats
=
first
)
if
s
is
None
:
continue
procstat
.
update
(
s
)
# Add a specific time_since_update stats for bitrate
procstat
[
'time_since_update'
]
=
time_since_update
# Update process list
self
.
processlist
.
append
(
procstat
)
# Next...
first
=
False
# Clean internals caches if timeout is reached
if
self
.
cache_timer
.
finished
():
...
...
@@ -437,6 +649,10 @@ class GlancesProcesses(object):
"""Get the processlist."""
return
self
.
processlist
def
gettree
(
self
):
"""Get the process tree."""
return
self
.
process_tree
def
getsortkey
(
self
):
"""Get the current sort key"""
if
self
.
getmanualsortkey
()
is
not
None
:
...
...
@@ -455,6 +671,8 @@ class GlancesProcesses(object):
def
setmanualsortkey
(
self
,
sortedby
):
"""Set the current sort key for manual sort."""
self
.
processmanualsort
=
sortedby
if
self
.
_enable_tree
and
(
self
.
process_tree
is
not
None
):
self
.
process_tree
.
setSorting
(
sortedby
,
sortedby
!=
"name"
)
return
self
.
processmanualsort
def
setautosortkey
(
self
,
sortedby
):
...
...
glances/core/glances_standalone.py
浏览文件 @
fafcd176
...
...
@@ -49,10 +49,14 @@ class GlancesStandalone(object):
if
args
.
process_filter
is
not
None
:
glances_processes
.
set_process_filter
(
args
.
process_filter
)
# Ignore kernel threads in process list
if
(
not
is_windows
)
and
args
.
no_kernel_threads
:
# Ignore kernel threads in process list
glances_processes
.
disable_kernel_threads
()
if
args
.
process_tree
:
# Enable process tree view
glances_processes
.
enable_tree
()
# Initial system informations update
self
.
stats
.
update
()
...
...
glances/outputs/glances_curses.py
浏览文件 @
fafcd176
...
...
@@ -431,7 +431,7 @@ class _GlancesCurses(object):
# Adapt number of processes to the available space
max_processes_displayed
=
screen_y
-
11
-
\
self
.
get_stats_display_height
(
stats_alert
)
if
self
.
args
.
enable_process_extended
:
if
self
.
args
.
enable_process_extended
and
not
self
.
args
.
process_tree
:
max_processes_displayed
-=
4
if
max_processes_displayed
<
0
:
max_processes_displayed
=
0
...
...
@@ -715,7 +715,14 @@ class _GlancesCurses(object):
pass
else
:
# New column
x
=
x
+
len
(
m
[
'msg'
])
try
:
# Python 2: we need to decode to get real screen size because utf-8 special tree chars
# occupy several bytes
offset
=
len
(
m
[
'msg'
].
decode
(
"utf-8"
))
except
AttributeError
:
# Python 3: strings are strings and bytes are bytes, all is good
offset
=
len
(
m
[
'msg'
])
x
=
x
+
offset
# Compute the next Glances column/line position
self
.
next_column
=
max
(
self
.
next_column
,
x
+
self
.
space_between_column
)
...
...
glances/plugins/glances_processcount.py
浏览文件 @
fafcd176
...
...
@@ -125,6 +125,7 @@ class Plugin(GlancesPlugin):
else
:
msg
=
_
(
"sorted by {0}"
).
format
(
glances_processes
.
getmanualsortkey
())
ret
.
append
(
self
.
curse_add_line
(
msg
))
ret
[
-
1
][
"msg"
]
+=
", %s view"
%
(
"tree"
if
glances_processes
.
is_tree_enabled
()
else
"flat"
)
# Return the message with decoration
return
ret
glances/plugins/glances_processlist.py
浏览文件 @
fafcd176
此差异已折叠。
点击以展开。
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录