jupyter notebook可以一方面用来验证自己的数据分析想法,一方面可以用来向别人展示自己的分析结果。
刚开始用,所以不好给jupyter notebook下个完整的定义。目前的用法就是在上面执行和调试用来展示gdb跟踪数据的python代码。用pyecharts画出图来。
先是下载和安装了Anaconda。Anaconda包含了完整的jupyter notebook以及python环境,还有其他的一些我目前没用到的强大的数据分析方面的工具。
Anaconda支持多个平台,我在windows和linux上都安装成功了。
在windows环境下,还需要安装pyecharts。打开Anaconda Prompt或者Anaconda Powershell Prompt,这是两个命令行的环境。
安装pyecharts:
python.exe -m pip install pyecharts
之后需要升级的话,则:
python.exe -m pip install --upgrade pyecharts
在linux环境下还可以直接用pip命令,但是window环境下直接用pip.exe的话,在我的环境中没有任何提示,但是貌似也能安装成功,不太清楚是怎么回事。
安装好以后就可以启动jupyter notebook了。
可以先启动Anaconda Nivagator,在其中再启动jupyter notebook。
也可以直接启动jupyter notebook。windows有对应的jupyter notebook启动菜单,linux中有同名的命令。
启动后就可以建目录,建脚本了。
这是代码和数据所在目录
那个.ipynb就是jupyter notebook的文件,保存了有格式的展示页面,包括代码和展示内容。打开看一下:
下面是全部代码,小学时候写作文就喜欢写流水账,发现写代码的时候也容易写成流水账。
不过流水账要是不太长的话,应该是容易理解的。
# -*- coding: UTF-8 -*-
import json
from pyecharts import options as opts
from pyecharts.charts import Graph, Page
from pyecharts.charts import Bar, Grid, Line,Scatter
class GdbGraph(object):
#初始化,从json格式的文件中读出所有节点和连接数据
#文件中每行数据是一次断点收集的数据
def __init__(self,nodesfile,linksfile,cmdsfile):
self.__allnodes = []
with open(nodesfile,"r") as nodesjf:
for line in nodesjf.readlines():
self.__allnodes.append(json.loads(line))
self.__alllinks = []
with open(linksfile,"r") as linksjf:
for line in linksjf.readlines():
self.__alllinks.append(json.loads(line))
self.__allcmds = []
with open(cmdsfile,"r") as cmdsjf:
for line in cmdsjf.readlines():
self.__allcmds.append(json.loads(line))
self.__graph = Graph()
#按入度数查找分割位置
#nodes_for_indegree是需要计算入度数的节点列表
def __getStop(self,nodes_for_indegree,start,breaks,max_indegrees):
indegrees = {}
#print(nodes_for_indegree)
#入度数清零
for n in nodes_for_indegree:
indegrees[n] = 0
stop = start
links = []
#查找分割位置
stop_found = False
for i in range(start,breaks):
for l in self.__alllinks[i]:
#要去掉重复的连接,两次断点之间的调用栈有很多是重复的,不能重复计算入度数
if(l not in links):
links.append(l)
if(l["target"] in indegrees.keys()):
indegrees[l["target"]] += 1
if( indegrees[l["target"]] > max_indegrees ):
stop_found = True
stop = i
#print(start,stop)
#print(l["source"],l["target"])
break
if(stop_found):
break
return stop
#按照入度数分割成多个图,比如在同一个图中,函数A被2个不同的函数调用,则节点A的入度数是2
#入度数越高,图就越复杂,不直观了
#目前只计算断点所在函数被调用的次数
def divdeGraph(self,max_indegrees=1):
breaks = len(self.__alllinks)
nodes_for_indegree = []
#按函数名字来建立一个用来记录入度数的字典
for nodes in self.__allnodes:
#只统计断点触发所在函数的入度数
n = nodes[0]["name"]
if( n not in nodes_for_indegree):
nodes_for_indegree.append(n)
#print(indegrees)
#用来保存分割以后的每段数据的开始位置
start_pos = []
start = 0
#用loops变量避免死循环
loops = 0
while( start < breaks and loops < 200 ):
loops +=1
stop = start
#如果命令行有输出,也要进行分割,用来指明当前断点有输出了
#start > 0是避开第一行,一开始肯定是有命令行输入的,不是断点造成的
if( start > 0 and len(self.__allcmds[start]) > 0 ):
stop = start
else:
stop = self.__getStop(nodes_for_indegree,start,breaks,max_indegrees)
#记录当前分割的开始位置
start_pos.append(start)
#一个断点就可能出现一个函数被调用多次的情况,要避免死循环
#在仅统计断点所在函数的入度数时不会出现这种情况
if(start==stop):
stop += 1
#下一个分割的开始位置
start = stop
return start_pos
def getGraph( self,start=0,count=0 ) -> Graph:
nodes = []
links = []
nodeobjs = []
linkobjs = []
if(count==0 or start+count > len(self.__alllinks)):
count = len(self.__alllinks) - start
#去掉重复的点和边
for i in range(start,start+count):
for n in self.__allnodes[i]:
if(n not in nodes):
nodes.append(n)
#断点节点尺寸加大
if(n==self.__allnodes[i][0]):
ssize = 20
else:
ssize = 10
node = opts.GraphNode(name=n["name"],symbol_size=ssize)
nodeobjs.append(node)
for l in self.__alllinks[i]:
if(l not in links):
links.append(l)
for l in links:
linestyle = opts.LineStyleOpts(curve=0.2,width=2)
link = opts.GraphLink( source=l["source"], target=l["target"], linestyle_opts=linestyle )
linkobjs.append(link)
subtitle = ""
for line in self.__allcmds[start:start+count]:
subtitle += "".join(line)
c = (
self.__graph
#.add("", nodes, links, repulsion=2000, edge_symbol=["","arrow"] ,gravity=1)
.add("", nodeobjs, linkobjs, repulsion=2000, edge_symbol=["","arrow"] ,gravity=4)
.set_global_opts(title_opts=opts.TitleOpts(title="mysqld gdb 断点触发序号" + str(start) + "(鼠标滚轮放大缩小;左键拖拽移动图形)",subtitle=subtitle))
#.set_global_opts(title_opts=opts.TitleOpts(title="mysqld gdb 断点触发序号" + str(start) + "(鼠标滚轮放大缩小;左键拖拽移动图形)nnSELECT * FROM TTTnUPDATE aaa SETn"))
)
return c
def getBreaksCount( self ):
return len(self.__alllinks)
gdb = GdbGraph("nodes.json","links.json","mysql_diff.json")
print("断点触发次数:",gdb.getBreaksCount())
bigpage=Page(interval = 20)
graph_start = gdb.divdeGraph(1)
length = len(graph_start)
for i in range(length):
start = graph_start[i]
count = gdb.getBreaksCount() - start
if(i<length-1):
count = graph_start[i+1] - start
bigpage.add(GdbGraph("nodes.json","links.json","mysql_diff.json").getGraph(start,count))
print(" ( ´´ิ∀´ิ` ) 鼠标单击输出的左侧部分,可以展开或者收缩整个输出 ( ´´ิ∀´ิ` ) ")
bigpage.render_notebook()
本例中,一共生成了15张图。切分的规则是断点(break point)所在函数节点的入度最多是1。也就是说不同函数对同一断点所在函数的调用会分在不同的图里。
之前还尝试了一下,所有节点的入度都要最多是1,那样会切分出更多的图。
还把mysql客户端的输入输出与同一时刻的断点信息放在一个图里。目的是为了看到,比如哪一个断点出现的时候客户端有返回信息了,那个命令发起的时候,触发了哪些断点。
上几个图
上图中从浅灰色的字能看到,是执行了update以后触发了断点。两个大圆点,就是设置了断点的函数,可以放大看一下。
不但可以放大缩小和移动,鼠标放到节点上,就只显示该节点以及与其连接的节点。
再截一个简单的图
再找一个客户端得到反馈的图
断点停在这里的时候,客户端得到返回信息了。
先写到这。
我们ACMUG北京5月25日的活动马上就开始了,快报名吧,每次活动我都是希望仔细学习一下的,然而又无法只顾自己听课。
【北京】数据库热点话题公开研讨会-信通院专场www.huodongxing.com