作者: 御风而行

站名: 一龙高清

分类: 电影&电视剧&综艺&动漫

图片: data-original="&&"

标题: title="&&"

副标题: class="pic-text text-right">&&</span>

搜索图片: data-original="&&"

搜索标题: title="&&"

搜索副标题: class="pic-text text-right">&&</span>

线路数组: data-toggle=&&</li>

播放数组: <ul class="stui-content__playlist&&

剧情

key: class

n: 全部

n: 古装

n: 战争

n: 青春偶像

v: /class/青春偶像

n: 喜剧

n: 家庭

n: 犯罪

n: 动作

n: 奇幻

n: 剧情

n: 历史

n: 经典

n: 乡村

n: 情景

n: 商战

n: 网剧

n: 其他

地区

key: area

n: 大陆

n: 韩国

n: 香港

n: 台湾

n: 日本

n: 美国

n: 泰国

n: 英国

n: 新加坡

年份

key: year

排序

key: by

n: 时间

n: 人气

n: 评分

剧情

n: 爱情

n: 恐怖

n: 科幻

n: 警匪

n: 动画

n: 武侠

n: 冒险

n: 枪战

n: 悬疑

n: 惊悚

n: 青春

n: 文艺

n: 微电影

n: 运动

n: 农村

n: 儿童

n: 网络电影

v: /class/网络电影

地区

n: 法国

n: 德国

n: 印度

n: 意大利

n: 西班牙

n: 加拿大


code:
{
    "规则名": "中国蓝光网",
    "规则作者": "",
    "请求头参数": "手机",
    "网页编码格式": "UTF-8",
    "图片是否需要代理": "0",
    "是否开启获取首页数据": "1",
    "首页推荐链接": " http://www.languangdy.com/",
    "首页列表数组规则": "body&&.post.box",
    "首页片单列表数组规则": ".thumbnail",
    "首页片单是否Jsoup写法": "1",
    "分类起始页码": "1",
    "分类链接": "http://www.languangdy.com/{cateId}/index_{catePg}.html[firstPage=http://www.languangdy.com/{cateId}/]",
    "分类名称": "Bluray蓝光原盘&4K超高清&蓝光1080&WEB-DL高清电影&TVshow&电影合集&其他资源",
    "分类名称替换词": "Bluray&4K&1080P&WEB-DL&other/TVshow&other/Collection&other",
    "筛选数据": {},
    //"筛选数据": "ext",
    //{cateId}
    "筛选子分类名称": "",
    "筛选子分类替换词": "",
    //{class}
    "筛选类型名称": "",
    "筛选类型替换词": "*",
    //{area}
    "筛选地区名称": "",
    "筛选地区替换词": "*",
    //{year}
    "筛选年份名称": "",
    "筛选年份替换词": "*",
    //{lang}
    "筛选语言名称": "",
    "筛选语言替换词": "*",
    //{by}
    "筛选排序名称": "时间&人气&评分",
    "筛选排序替换词": "time&hits&score",
    "分类截取模式": "1",
    "分类列表数组规则": "body&&.post.box",
    "分类片单是否Jsoup写法": "1",
    "分类片单标题": "a&&title",
    "分类片单链接": "a&&href",
    "分类片单图片": "img&&src",
    "分类片单副标题": ".info_category info_ico&&Text",
    "分类片单链接加前缀": "",
    "分类片单链接加后缀": "",
    "搜索请求头参数": "手机",
    "搜索链接": " http://www.languangdy.com/e/search/index.php;post",
    "POST请求数据": "show=title&tempid=1&tbname=article&mid=1&dopost=search&submit=&keyboard={wd}",
    "搜索截取模式": "1",
    "搜索列表数组规则":  "body&&.thumbnail",
    "搜索片单是否Jsoup写法": "1",
    "搜索片单图片":  "img&&src",
    "搜索片单标题":"a&&title!<font color='red'>",
    "搜索片单链接":"a&&href",
    "搜索片单副标题": ".info_category info_ico&&Text",
    "搜索片单链接加前缀": "",
    "搜索片单链接加后缀": "",
    "链接是否直接播放": "0",
    "直接播放链接加前缀": "",
    "直接播放链接加后缀": "",
    "直接播放直链视频请求头": "",
    "详情是否Jsoup写法": "1",
    "类型详情": "body&&.context&&p:contains(类)&&Text!",
    "年代详情": "body&&.context&&p:contains(年)&&Text!",
    "地区详情": "body&&.context&&p:contains(产)&&Text!",
    "演员详情": "body&&.context&&p:contains(演)&&Text!",
    "简介详情": "body&&.context&&p:contains(，)&&Text!",
    "线路列表数组规则": "body&&.dldiv",
    "线路标题": "a&&Text",
    "播放列表数组规则": "body&&.dldiv",
    "选集列表数组规则": "a&&[href^=magnet]",
    "选集标题链接是否Jsoup写法": "1",
    "选集标题": "a&&Text",
    "选集链接": "a&&href",
    "是否反转选集序列": "0",
    "选集链接加前缀": "",
    "选集链接加后缀": "",
    "分析MacPlayer": "0",
    "是否开启手动嗅探": "0",
    "手动嗅探视频链接关键词": ".mp4#.m3u8#.flv#video/tos",
    "手动嗅探视频链接过滤词": ".html#=http"
}

license: unknown


code:
//写法思路来自biubiu影院，海阔视界，xpath筛选。2022年9月22日
//项目魔改至github开源仓库https://github.com/Tangsan99999/TvJar中的XBiubiu.java文件，
//本文档为完整模板，实际情况可按规则写法删去不需要的。
{
    //规则名
    "title": "云盘分享",
    //作者
    "author": "聆听优美旋律",

    //搜索专属请求头UA,不填则默认okhttp/3.12.11，可填MOBILE_UA或PC_UA使用内置的手机版或电脑版UA
    //多个请求头参数写法示例，"User-Agent$PC_UA#Referer$http://ww.baidu.com#Cookie$ser=ok",
    //习惯查看手机源码写建议用手机版UA，习惯查看PC版源码写建议用电脑版UA
    "SUserAgent":"PC_UA",
    //搜索链接，搜索关键字用{wd}表示，post请求的最后面加;post
    //POST链接示例 http://www.lezhutv.com/index.php?m=vod-search;post
    "search_url": "https://www.yunpanfenxiang.cn/api/bbs/api/getdata2?key={wd}",
    //POST搜索body，填写搜索关键字的键值，一般常见的是searchword和wd，不是POST搜索的可留空或删除。
    "sea_PtBody":"",

    //搜索模式,0为json搜索，只支持列表在list数组里的，其它为网页截取。
    "search_mode": "1",

    //搜索是否二次截取
    "sea_YN_twice": "0",
    //搜索二次截取前
    "sea_twice_pre": "空",
    //搜索二次截取后
    "sea_twice_suf": "</ul>",

    //搜索数组前缀
    "sea_arr_pre": "page_",
    //搜索数组后缀
    "sea_arr_suf": "content",
    //搜索片单图片，截取前缀&&截取后缀
    "sea_pic": "",
    //搜索片单标题，截取前缀&&截取后缀
    "sea_title": "title\":\"&&\"",
    //搜索片单链接，截取前缀&&截取后缀
    "sea_url": "url\":\"&&\"",
    //搜索副标题，截取前缀&&截取后缀
    "sea_subtitle": "",

    //搜索片单链接补前缀
    "search_prefix": "",
    //搜索片单链接补后缀，这个一般json搜索的需要
    "search_suffix": ""

}


搜索请求头参数: 手机

搜索列表数组规则: #flarum-content&&li

搜索片单标题: a&&Text

搜索片单链接: a&&href

播放列表数组规则: body&&#flarum-content

选集标题: a&&Text

选集链接: a&&href

手动嗅探视频链接过滤词: .html#=http


规则名: 云镜影视

规则作者: 香雅情

首页片单列表数组规则: .public-list-box

首页片单标题: a&&title

首页片单链接: a&&href

首页片单图片: .lazy&&data-original

首页片单副标题: .public-list-prb&&Text

分类名称: 电影&电视剧&综艺&动漫

筛选排序名称: 时间&人气&评分

筛选排序替换词: time&hits&score

分类列表数组规则: list

分类片单链接加后缀: .html

搜索请求头参数: User-Agent$手机

搜索片单图片: .lazy&&data-original

搜索片单标题: .thumb-txt&&Text

搜索片单副标题: .public-list-prb&&Text

直接播放链接加后缀: #isVideo=true#

演员详情: .detail-info&&.slide-info,2&&Text!演员 :

线路列表数组规则: .anthology-tab&&a

线路标题: Text

选集列表数组规则: li


站名: 京广航影视

搜索数组: class="detail">&&</a>

影片地区: 地区：&&

影片年代: 年份：&&

影片类型: 更新：&&

导演: 导演：&&

主演: 主演：&&

简介: display: none;">&&</span>

剧情

n: 全部

n: 爱情

n: 剧情

n: 喜剧

n: 家庭

n: 音乐

n: 歌舞

n: 动漫

n: 西部

n: 武侠

n: 古装

n: 动作

n: 恐怖

n: 冒险

n: 犯罪

n: 记录

n: 战争

n: 历史

n: 传记

n: 体育

n: 科幻

n: 魔幻

n: 奇幻片

n: 大陆

n: 香港

n: 台湾

n: 美国

n: 英国

n: 日本

n: 韩国

n: 泰国

n: 其他

排序

n: 时间

n: 人气

n: 评分

剧情

n: 偶像

n: 奇幻

n: 经典

n: 乡村

n: 情景

n: 商战

n: 网剧

n: 内地

n: 新加坡


code:
{
    "规则名": "出差番",
    "规则作者": "古月",
    "请求头参数": "手机",
    "网页编码格式": "UTF-8",
    "图片是否需要代理": "0",
    "是否开启获取首页数据": "1",
    "首页推荐链接": "https://www.xn--79q440a.xyz/",
    "首页列表数组规则": "body&&.module-items",
    "首页片单列表数组规则": ".module-item",
    "首页片单是否Jsoup写法": "1",
    "分类起始页码": "1",
    "分类链接": "https://www.xn--79q440a.xyz/index.php/vod-show-id-{cateId}-page-{catePg}.html",
    "分类名称": "番剧&BDRip&剧场版",
    "分类名称替换词": "1&3&4",
    "筛选数据": {},
    //"筛选数据": "ext",
    //{cateId}
    "筛选子分类名称": "",
    "筛选子分类替换词": "",
    //{class}
    "筛选类型名称": "",
    "筛选类型替换词": "*",
    //{area}
    "筛选地区名称": "",
    "筛选地区替换词": "*",
    //{year}
    "筛选年份名称": "",
    "筛选年份替换词": "*",
    //{lang}
    "筛选语言名称": "",
    "筛选语言替换词": "*",
    //{by}
    "筛选排序名称": "",
    "筛选排序替换词": "",
    "分类截取模式": "1",
    "分类列表数组规则": ".module-items&&.module-item",
    "分类片单是否Jsoup写法": "1",
    "分类片单标题": "a&&title",
    "分类片单链接": "a&&href",
    "分类片单图片": ".lazyloaded&&data-src",
    "分类片单副标题": ".module-item-text&&Text",
    "分类片单链接加前缀": "https://www.xn--79q440a.xyz",
    "分类片单链接加后缀": "",
    "搜索请求头参数": "User-Agent$手机",
    "搜索链接": "https://www.xn--79q440a.xyz/index.php/vod-search-page-{catePg}-wd-{wd}.html",
    "POST请求数据": "",
    "搜索截取模式": "1",
    "搜索列表数组规则": "body&&.module-search-item",
    "搜索片单是否Jsoup写法": "1",
    "搜索片单图片": ".lazyloaded&&data-src",
    "搜索片单标题": "img&&alt",
    "搜索片单链接": "a&&href",
    "搜索片单副标题": ".video-serial&&Text",
    "搜索片单链接加前缀": "https://www.xn--79q440a.xyz",
    "搜索片单链接加后缀": "",
    "链接是否直接播放": "0",
    "直接播放链接加前缀": "",
    "直接播放链接加后缀": "",
    "直接播放直链视频请求头": "",
    "详情是否Jsoup写法": "1",
    "类型详情": ".tag-link,1&&Text",
    "年代详情": ".tag-link,2&&Text",
    "地区详情": ".tag-link,3&&Text",
    "演员详情": "",
    "简介详情": "body&&.video-info-items:contains(剧情：)&&Text",
    "线路列表数组规则": "body&&.module-tab-item",
    "线路标题": "span&&Text",
    "播放列表数组规则": "body&&.sort-item",
    "选集列表数组规则": "a",
    "选集标题链接是否Jsoup写法": "1",
    "选集标题": "a&&Text",
    "选集链接": "a&&href",
    "是否反转选集序列": "0",
    "选集链接加前缀": "https://www.xn--79q440a.xyz",
    "选集链接加后缀": "",
    "分析MacPlayer": "0",
    "是否开启手动嗅探": "1",
    "手动嗅探视频链接关键词": ".mp4#.m3u8#.flv#video/tos#ipfs",
    "手动嗅探视频链接过滤词": ".html#=http"
}


code:
{
    "规则名": "大豆",
    "规则作者": "",
    "请求头参数": "User-Agent$PC_UA",
    "网页编码格式": "UTF-8",
    "图片是否需要代理": "0",
    "是否开启获取首页数据": "1",
    "首页推荐链接": "https://www.dadou.pro",
    "首页列表数组规则": "body&&.fadeInUp:has(.gen-movie-img)",
    "首页片单列表数组规则": ".public-list-box",
    "首页片单是否Jsoup写法": "1",
    "首页片单标题": "a&&title",
    "首页片单链接": "a&&href",
    "首页片单图片": ".lazy&&data-original",
    "首页片单副标题": ".public-list-prb&&Text",
    "首页片单链接加前缀": "https://www.dadou.pro",
    "首页片单链接加后缀": "",
    "分类起始页码": "1",
    "分类链接": "https://www.dadou.pro/index.php/api/vod?type={cateId}&page={catePg}&time=时间戳&key=md5(DS时间戳DCC147D11943AF75);post",
    "分类名称": "电影&剧集&综艺&动漫&记录",
    "分类名称替换词": "1&2&3&4&39",
    "筛选数据": "ext",
    //{cateId}
    "筛选子分类名称": "",
    "筛选子分类替换词": "",
    //{class}
    "筛选类型名称": "NETFLIX&喜剧&爱情&犯罪&剧情&惊悚&恐怖&悬疑&奇幻&动作&科幻&冒险&家庭&灾难&同性&BBC&HBO&HULU&ONE||NETFLIX&BBC&HBO&HULU&ONE&喜剧&犯罪&剧情&惊悚&恐怖&悬疑&奇幻&动作&科幻&冒险||NETFLIX&真人秀&纪录片&人物&自然&历史探索||NETFLIX&喜剧&犯罪&剧情&冒险&惊悚&恐怖&悬疑&奇幻&动作&科幻&运动||真人秀&纪录片&纪录片人物&纪录片NETFLIX&BBC&自然&历史&探索",
    "筛选类型替换词": "*",
    //{area}
    "筛选地区名称": "大陆&香港&台湾&美国&法国&英国&日本&韩国&德国&泰国&印度&意大利&西班牙&加拿大&其他||韩国&美国&泰国&英国&香港&台湾&日本&新加坡&内地&其他||大陆&韩国&美国&香港&台湾&日本&泰国&英国&新加坡&其他&内地||日本&大陆&韩国&香港&台湾&美国&英国&新加坡&其他&泰国||大陆&内地&韩国&香港&台湾&日本&美国&泰国&英国&新加坡&其他",
    "筛选地区替换词": "*",
    //{year}
    "筛选年份名称": "",
    "筛选年份替换词": "*",
    //{lang}
    "筛选语言名称": "",
    "筛选语言替换词": "*",
    //{by}
    "筛选排序名称": "最新&最热&评分",
    "筛选排序替换词": "time&hits&score",
    "分类截取模式": "0",
    "分类列表数组规则": "list",
    "分类片单是否Jsoup写法": "1",
    "分类片单标题": "vod_name",
    "分类片单链接": "vod_id",
    "分类片单图片": "vod_pic",
    "分类片单副标题": "vod_remarks",
    "分类片单链接加前缀": "https://www.dadou.pro/Vod/",
    "分类片单链接加后缀": ".html",
    "搜索请求头参数": "User-Agent$PC_UA",
    "搜索链接": "https://dadou.pro/index.php/ajax/suggest?mid=1&wd={wd}&limit=10",
    "POST请求数据": "wd={wd}",
    "搜索截取模式": "0",
    "搜索列表数组规则": "list",
    "搜索片单是否Jsoup写法": "1",
    "搜索片单图片": "pic",
    "搜索片单标题": "name",
    "搜索片单链接": "id",
    "搜索片单副标题": "",
    "搜索片单链接加前缀": "https://www.dadou.pro/Vod/",
    "搜索片单链接加后缀": ".html",
    "链接是否直接播放": "0",
    "直接播放链接加前缀": "",
    "直接播放链接加后缀": "#isVideo=true#",
    "直接播放直链视频请求头": "",
    "详情是否Jsoup写法": "1",
    "类型详情": "",
    "年代详情": "",
    "地区详情": "",
    "演员详情": ".detail-info&&.slide-info,2&&Text!演员：",
    "简介详情": "#height_limit&&Text",
    "线路列表数组规则": ".anthology-tab&&a",
    "线路标题": "Text",
    "播放列表数组规则": "body&&.anthology-list-play",
    "选集列表数组规则": "li",
    "选集标题链接是否Jsoup写法": "1",
    "选集标题": "a&&Text",
    "选集链接": "a&&href",
    "是否反转选集序列": "1",
    "选集链接加前缀": "https://www.dadou.pro",
    "选集链接加后缀": "",
    "分析MacPlayer": "0",
    "是否开启手动嗅探": "0",
    "手动嗅探视频链接关键词": ".mp4#.m3u8#.flv",
    "手动嗅探视频链接过滤词": ".html#=http#=https"
}


站名: 巨人影院

分类: 电影&电视剧&综艺&动漫

搜索后缀: /detail/

播放数组: <ul class="myui-content__list&&

n: 全部

n: 喜剧

v: 喜剧

n: 爱情

v: 爱情

n: 恐怖

v: 恐怖

n: 动作

v: 动作

n: 科幻

v: 科幻

n: 剧情

v: 剧情

n: 战争

v: 战争

n: 警匪

v: 警匪

n: 犯罪

v: 犯罪

n: 动画

v: 动画

v: 奇幻

n: 武侠

v: 武侠

n: 冒险

v: 冒险

n: 枪战

v: 枪战

n: 悬疑

v: 悬疑

n: 惊悚

v: 惊悚

v: 经典

n: 青春

v: 青春

n: 文艺

v: 文艺

n: 微电影

v: 微电影

n: 古装

v: 古装

n: 历史

v: 历史

n: 运动

v: 运动

n: 农村

v: 农村

n: 儿童

v: 儿童

n: 网络电影

v: 网络电影

地区

n: 大陆

v: 大陆

n: 香港

v: 香港

n: 台湾

v: 台湾

n: 美国

v: 美国

n: 法国

v: 法国

n: 英国

v: 英国

v: 日本

v: 韩国

n: 德国

v: 德国

v: 泰国

n: 印度

v: 印度

n: 意大利

v: 意大利

n: 西班牙

v: 西班牙

n: 加拿大

v: 加拿大

v: 其他


规则名: 异视琳琅

请求头参数: User-Agent$电脑

首页片单列表数组规则: .pack-ykpack

分类名称: 电影&电视剧&动漫&综艺&记录

筛选数据: ext

分类片单标题: .vodname&&a&&Text

分类片单链接: a&&href

分类片单图片: .eclazy&&data-original

分类片单副标题: .pack-prb&&Text

搜索请求头参数: User-Agent$电脑

搜索列表数组规则: .ec-search&&li

搜索片单图片: .eclazy&&data-original

搜索片单标题: a&&title

搜索片单副标题: .author&&Text

地区详情: 地区：&&

演员详情: 演员：&&

简介详情: 简介：&&

线路标题: a&&Text

选集列表数组规则: a

选集标题: Text

选集链接: href

手动嗅探视频链接过滤词: .html#?http


规则名: 星辰影院

请求头参数: 手机

是否开启获取首页数据: 是

首页片单列表数组规则: li

分类名称: 电影&电视剧&综艺&动漫

分类名称替换词: dianying&dianshiju&zongyi&dongman

筛选子分类名称: 动作片&爱情片&科幻片&恐怖片&喜剧片&剧情片&在线直播||国产剧&港台剧&欧美剧&日韩剧

分类列表数组规则: .stui-vodlist&&li

分类片单图片: .lazyload&&data-original

分类片单副标题: .pic-text&&Text

POST请求数据: searchword={wd}

搜索片单图片: .lazyload&&data-original

搜索片单副标题: .pic-text&&Text

类型详情: 类型：&&

年代详情: 年份：&&

演员详情: 主演：&&

线路列表数组规则: body&&#playlist


搜索片单标题: a[data-type=ali]&&data-title

播放列表数组规则: html&&head


code:
{
    "规则名": "油壶电影",
    "规则作者": "",
    "请求头参数": "手机",
    "网页编码格式": "UTF-8",
    "图片是否需要代理": "0",
    "是否开启获取首页数据": "1",
    "首页推荐链接": "https://www.yohu.tv",
    "首页列表数组规则": "body&&.stui-vodlist:has(h4)",
    "首页片单列表数组规则": "li",
    "首页片单是否Jsoup写法": "1",
    "分类起始页码": "1",
    "分类链接": "https://www.yohu.tv/vodtype/{cateId}-{catePg}.html",
    "分类名称": "电影&电视剧&综艺&动漫&纪录",
    "分类名称替换词": "1&2&3&4&5",
    //"筛选数据": {},
    "筛选数据": "ext",
    //{cateId}
    "筛选子分类名称": "",
    "筛选子分类替换词": "",
    //{class}
    "筛选类型名称": "动作片&喜剧片&爱情片&科幻片&恐怖片&剧情片&战争片&其他片||国产剧&日韩剧&欧美剧&港台剧&泰剧&记录片||综艺||动漫",
    "筛选类型替换词": "*",
    //{area}
    "筛选地区名称": "大陆&香港&台湾&美国&法国&英国&日本&韩国&德国&泰国&印度&意大利&西班牙&加拿大&其他||中国大陆&韩国&香港&台湾&日本&美国&泰国&英国&西班牙&新加坡&其他||内地&港台&日韩&欧美||国产&日本&欧美&其他",
    "筛选地区替换词": "*",
    //{year}
    //"筛选年份名称": "2022&2021&2020&2019&2018&2017&2016&2015&2014&2013&2012&2011&2010",
    //"筛选年份替换词": "*",
    //{lang}
    "筛选语言名称": "国语&英语&粤语&闽南语&韩语&日语&法语&德语&其它||国语&英语&粤语&闽南语&韩语&日语&其它||国语&英语&粤语&闽南语&韩语&日语&其它||国语&英语&粤语&闽南语&韩语&日语&其它",
    "筛选语言替换词": "*",
    //{by}
    //"筛选排序名称": "时间&人气&评分",
    //"筛选排序替换词": "time&hits&score",
    "分类截取模式": "1",
    "分类列表数组规则": ".stui-vodlist&&li",
    "分类片单是否Jsoup写法": "1",
    "分类片单标题": "h4&&a&&Text",
    "分类片单链接": "h4&&a&&href",
    "分类片单图片": ".lazyload&&data-original",
    "分类片单副标题": ".pic-text&&Text",
    "分类片单链接加前缀": "https://www.yohu.tv",
    "分类片单链接加后缀": "",
    "搜索请求头参数": "User-Agent$手机",
    "搜索链接": "https://www.yohu.tv/vodsearch/{wd}-------------.html",
    "POST请求数据": "",
    "搜索截取模式": "1",
    "搜索列表数组规则": ".stui-vodlist&&li",
    "搜索片单是否Jsoup写法": "1",
    "搜索片单图片": ".lazyload&&data-original",
    "搜索片单标题": "h4&&a&&Text",
    "搜索片单链接": "h4&&a&&href",
    "搜索片单副标题": ".pic-text&&Text",
    "搜索片单链接加前缀": "https://www.yohu.tv",
    "搜索片单链接加后缀": "",
    "链接是否直接播放": "0",
    "直接播放链接加前缀": "",
    "直接播放链接加后缀": "",
    "直接播放直链视频请求头": "",
    "详情是否Jsoup写法": "1",
    "类型详情": "类型：&&<",
    "年代详情": "年份：&&<",
    "地区详情": "地区：&&<",
    "演员详情": ".stui-content__detail&&p,6&&Text!主演：",
    "简介详情":".stui-content__detail&&p,8&&Text!简介：",
    "线路列表数组规则": ".nav.nav-tabs.dpplay.pull-right&&li",
    "线路标题": "a&&Text",
    "播放列表数组规则": "body&&.stui-content__playlist",
    "选集列表数组规则": "li",
    "选集标题链接是否Jsoup写法": "1",
    "选集标题": "a&&Text",
    "选集链接": "a&&href",
    "是否反转选集序列": "0",
    "选集链接加前缀": "https://www.yohu.tv",
    "选集链接加后缀": "",
    "分析MacPlayer": "2",
    "是否开启手动嗅探": "0",
    "手动嗅探视频链接关键词": ".mp4#.m3u8#.flv#video/tos",
    "手动嗅探视频链接过滤词": ".html#=http"
}


code:
{
    "规则名": "玩偶哥哥",
    "规则作者": "香雅情",
    "请求头参数": "MOBILE_UA",
    "网页编码格式": "UTF-8",
    "图片是否需要代理": "否",
    "是否开启获取首页数据": "是",
    "首页推荐链接": "http://wogg.xyz",
    "首页列表数组规则": "body&&.module-items",
    "首页片单列表数组规则": ".module-item:not(:contains(饭太硬))",
    "首页片单是否Jsoup写法": "1",
    "分类起始页码": "1",
    "分类链接": "http://wogg.xyz/index.php/vodshow/{cateId}--{by}------{catePg}---{year}.html",
    "分类名称": "电影&电视剧&综艺&动漫&音乐空间",
    "分类名称替换词": "1&20&28&24&32",
    //"筛选数据": {},
    "筛选数据": "ext",
    //{cateId}
    "筛选子分类名称": "站长推荐电影&动作犯罪&剧情喜剧&科幻神话&恐怖悬疑&战争军事||站长推荐剧集&欧美剧&国产剧&日韩剧&港台剧||空||站长推荐动漫&国产动漫&日韩动漫&欧美动漫",
    "筛选子分类替换词": "35&6&7&30&29&12||33&22&21&23&31||空||36&25&26&27",
    //{class}
    "筛选类型名称": "",
    "筛选类型替换词": "*",
    //{area}
    "筛选地区名称": "",
    "筛选地区替换词": "*",
    //{year}
    "筛选年份名称": "",
    "筛选年份替换词": "*",
    //{lang}
    "筛选语言名称": "",
    "筛选语言替换词": "*",
    //{by}
    "筛选排序名称": "时间&人气&评分",
    "筛选排序替换词": "time&hits&score",
    "分类截取模式": "1",
    "分类列表数组规则": ".module-items&&.module-item:not(:contains(饭太硬))",
    "分类片单是否Jsoup写法": "是",
    "分类片单标题": "a&&title",
    "分类片单链接": "a&&href",
    "分类片单图片": ".module-item-pic&&img&&data-src",
    "分类片单副标题": ".module-item-text&&Text",
    "分类片单链接加前缀": "http://wogg.xyz",
    "分类片单链接加后缀": "",
    "搜索请求头参数": "User-Agent$MOBILE_UA",
    "搜索链接": "http://wogg.xyz/index.php/vodsearch/{wd}----------1---.html",
    "POST请求数据": "",
    "搜索截取模式": "1",
    "搜索列表数组规则": ".module-items&&.module-search-item",
    "搜索片单是否Jsoup写法": "是",
    "搜索片单图片": ".lazyload&&data-src",
    "搜索片单标题": "h3&&Text",
    "搜索片单链接": "h3&&a&&href",
    "搜索片单副标题": ".video-info&&a&&Text",
    "搜索片单链接加前缀": "http://wogg.xyz",
    "搜索片单链接加后缀": "",
    "链接是否直接播放": "否",
    "直接播放链接加前缀": "",
    "直接播放链接加后缀": "",
    "直接播放直链视频请求头": "",
    "详情是否Jsoup写法": "是",
    "类型详情": "",
    "年代详情": "",
    "地区详情": "",
    "演员详情": ".video-info-main&&.video-info-actor,1&&Text",
    "简介详情": ".video-info-content&&p,-1&&Text",
    "线路列表数组规则": ".module-player-tab&&.module-tab-item",
    "线路标题": "span&&Text",
    "播放列表数组规则": "body&&.module-player-list",
    "选集列表数组规则": ".module-row-one",
    "选集标题链接是否Jsoup写法": "是",
    "选集标题": "h4&&Text",
    "选集链接": ".btn-down&&a&&href",
    "是否反转选集序列": "否",
    "选集链接加前缀": "",
    "选集链接加后缀": "",
    "分析MacPlayer": "",
    "是否开启手动嗅探": "否",
    "手动嗅探视频链接关键词": ".mp4#.m3u8#.flv#video/tos",
    "手动嗅探视频链接过滤词": ".html#=http"
}


code:
//写法思路来海阔视界，xpath筛选。本人是海阔用户，所以搬了海阔的jsoup写法过来。2022年9月17日
//jsoup规则写法请查阅海阔视界或者海阔影视相关教程。不支持js写法
//本文档为完整模板，请不要去无中生有添加多余的键值参数。
{
    //规则名
    "title": "电影港",
    //作者
    "author": "香雅情",
    //请求头UA,键名$键值，每一组用#分开，不填则默认okhttp/3.12.11，可填MOBILE_UA或PC_UA使用内置的手机版或电脑版UA
    //多个请求头参数写法示例，"User-Agent$PC_UA#Referer$http://ww.baidu.com#Cookie$ser=ok",每一组用#分开。
    //习惯查看手机源码写建议用手机版UA，习惯查看PC版源码写建议用电脑版UA
    "Headers":"PC_UA",
    //网页编码格式默认UTF-8编码，UTF-8，GBK，GB2312
    "Coding_format":"gb2312",
    //图片是否需要代理
    "PicNeedProxy":"0",
    //是否开启获取首页数据，0关闭，1开启
    "homeContent":"0",
    //分类链接起始页码,禁止负数和含小数点。
    "firstpage": "1",
    //分类链接,{cateId}是分类，{catePg}是页码,第一页没有页码的可以这样写 第二页链接[firstPage=第一页的链接]
    "class_url": "https://www.dygang.tv/{cateId}/index_{catePg}.htm[firstPage=https://www.dygang.cc/{cateId}/index.htm]",
    //分类名，分类1&分类2&分类3
    "class_name": "最新电影&经典高清&国配电影&经典港片&国剧&日韩剧&美剧&综艺&动漫&纪录片&高清原盘&4K高清区&3D电影&电影专题",
    //分类名替换词，替换词1&替换词2&替换词3，替换词包含英文&的用两个中文＆＆代替，示例：＆＆id=0&＆＆id=1
    "class_value": "ys&bd&gy&gp&dsj&dsj1&yx&zy&dmq&jilupian&1080p&4K&3d&dyzt",
    //筛选数据，json格式，参考xpath的筛选写法
    "filterdata":{},

    //分类页面截取数据模式，0为json，其它数字为普通网页。
    "cat_mode": "1",
    //分类列表数组定位，最多支持3层，能力有限，不是所有页面都能支持
    "cat_arr_rule": "body&&table[width=388]",
    //分类片单信息jsoup与xb截取写法切换，只作用于html网页，1为jsoup写法(默认)，0为xb写法
    "cat_is_jsoup":"1",
    //分类片单标题
    "cat_title": "img&&alt",
    //分类片单链接
    "cat_url": "a&&href",
    //分类片单图片，支持自定义图片链接
    "cat_pic": "img&&src",
    //分类片单副标题
    "cat_subtitle":"[align=center]&&Text",
    //分类片单链接补前缀
    "cat_prefix": "https://www.dygang.tv",
    //分类片单链接补后缀
    "cat_suffix": "",

    //搜索请求头参数,不填则默认okhttp/3.12.11，可填MOBILE_UA或PC_UA使用内置的手机版或电脑版UA
    //多个请求头参数写法示例，键名$键值，每一组用#分开。"User-Agent$PC_UA#Referer$http://ww.baidu.com#Cookie$ser=ok"。
    "SHeaders":"User-Agent$PC_UA#Content-Type$charset=gb2312",
    //搜索链接，搜索关键字用{wd}表示，post请求的最后面加;post
    //POST链接示例 http://www.lezhutv.com/index.php?m=vod-search;post
    "search_url": "https://www.dygang.tv/e/search/index123.php;post",
    //POST搜索body，填写搜索关键字的键值，一般常见的是searchword和wd，不是POST搜索的可留空或删除。
    "sea_PtBody":"keyboard={wd}&submit=搜+索&show=title,smalltext&tempid=1&tbname=article",

    //搜索截取模式,0为json搜索，只支持列表在list数组里的，其它数字为网页截取。
    "search_mode": "1",
    //搜索列表数组定位，不填默认内置list，最多支持3层，能力有限，不是所有页面都能支持。
    "sea_arr_rule": "body&&table[width=388]",
    //搜索片单信息jsoup与xb截取写法切换，只作用于html网页，1为jsoup写法(默认)，0为xb写法
    "sea_is_jsoup":"1",
    //搜索片单图片，支持自定义图片链接
    "sea_pic": "img&&src",
    //搜索片单标题
    "sea_title": "img&&alt",
    //搜索片单链接
    "sea_url": "a&&href",
    //搜索片单副标题
    "sea_subtitle":"",
    //搜索片单链接补前缀
    "search_prefix": "https://www.dygang.tv",
    //搜索片单链接补后缀，这个一般json搜索的需要
    "search_suffix": "",

    //片单链接是否直接播放，0否，1分类片单链接直接播放，2详情选集链接直接播放。
    //设置成直接播放后，后面3个参数请注意该留空的请务必留空。
    "force_play": "0",
    //直接播放链接补前缀
    "play_prefix": "",
    //直接播放链接补后缀，设置为#isVideo=true#可强制识别为视频链接
    "play_suffix": "",
    //直接播放链接设置请求头，只对直链视频有效，每一组用#分开
    "play_header": "",

    //项目信息jsoup与xb截取写法切换，1为jsoup写法(默认)，0为xb写法
    "proj_is_jsoup":"0",
    //类型数据，截取前缀&&截取后缀
    "proj_cate": "",
    //年代数据，截取前缀&&截取后缀
    "proj_year": "",
    //地区数据，截取前缀&&截取后缀
    "proj_area": "",
    //演员数据，截取前缀&&截取后缀
    "proj_actor": "演　　员&&</",
    //简介内容，截取前缀&&截取后缀
    "proj_plot": "简　　介&&</",

    //线路截取区域，如果不需要请把tab_title或tab_arr_rule置空或者全部不要填。
    //线路截取数组
    "tab_arr_rule": "#dede_content",
    //线路标题，截取前缀&&截取后缀
    "tab_title": "strong&&Text",

    //列表数组截取，必须
    "list_arr_rule": "#dede_content",
    //集数数组截取，必须
    "epi_arr_rule": "table&&[href*=magnet]",
    //集数标题，截取前缀&&截取后缀
    "epi_title": "a&&Text",
    //集数链接，截取前缀&&截取后缀
    "epi_url": "a&&href",
    //选集是否反转显示
    "epi_reverse": "0",
    //集数链接补前缀
    "epiurl_prefix": "",
    //集数链接补后缀
    "epiurl_suffix": "",

    //下面几个参数请勿乱用。否则可能会有副作用。
    //分析网页源码中有<script type="text/javascript">var player_aaaa={"flag":"play","encrypt这种源码的链接解析
    //如果网页源码里没有这种请设置为0
    "Anal_MacPlayer":"0",
    //是否开启手动嗅探，只对网页嗅探有效，0否，1是
    "ManualSniffer":"0",
    //手动嗅探视频链接关键字，每个用#隔开
    "VideoFormat":".mp4#.m3u8#.flv",
    //手动嗅探视频链接过滤关键词,每个用#隔开
    "VideoFilter":".html#=http"
}


作者: 那里花开

演员: 主演：&&

影片类型: module-info-tag"&&立即播放

线路数组: <div class="module-tab-item tab-item&&

线路标题: <span>&&</small>

播放数组: <div class="module-play-list-content&&

类型

key: cateId

n: 全部

n: 动作片

n: 喜剧片

n: 爱情片

n: 科幻片

n: 恐怖片

n: 剧情片

n: 战争片

排序

n: 时间

v: time

n: 人气

v: hits

n: 评分

v: score

剧情

n: 国产剧

n: 港台剧

n: 韩国剧

n: 欧美剧

n: 台湾剧

n: 日本剧

n: 泰国剧

n: 海外剧

n: 国产动漫

n: 日韩动漫

n: 欧美动漫

n: 港台动漫

n: 大陆综艺

n: 港台综艺

n: 日韩综艺

n: 欧美综艺


站名: 看看剧

副标题: pic-text text-right">&&</span>

搜索副标题: pic-text text-right">&&</b>

类型

n: 全部类型

n: 动作片

v: dongzuopian

n: 喜剧片

v: aiqingpian

n: 爱情片

v: kehuanpian

n: 科幻片

v: kongbupian

n: 恐怖片

n: 剧情片

v: juqingpian

n: 战争片

v: zhanzhengpian

n: 动画片

v: donghua

n: 全部剧情

n: Netflix

v: Netflix

n: 喜剧

v: 喜剧

n: 爱情

v: 爱情

n: 恐怖

v: 恐怖

n: 动作

v: 动作

n: 科幻

v: 科幻

n: 剧情

v: 剧情

n: 战争

v: 战争

n: 纪录片

v: 纪录片

n: 犯罪

v: 犯罪

n: 动画

v: 动画

n: 奇幻

v: 奇幻

n: 武侠

v: 武侠

地区

n: 全部地区

n: 中国大陆

v: 中国大陆

n: 中国香港

v: 中国香港

n: 中国台湾

v: 中国台湾

n: 美国

n: 韩国

n: 日本

n: 泰国

n: 新加坡

v: 新加坡

n: 马来西亚

v: 马来西亚

n: 印度

n: 英国

n: 法国

n: 德国

时间

n: 全部时间

类型

v: guochanju

v: gangtaiju

n: 日韩剧

v: rihanju

v: oumeiju

v: taiguoju


code:
{
    "规则名": "端木视频",
    "规则作者": "香雅情",
    "请求头参数": "MOBILE_UA",
    "网页编码格式": "UTF-8",
    "是否开启获取首页数据":"1",
    "图片是否需要代理": "否",
    "是否开启获取首页数据": "1",
    "首页推荐链接": "http://dmsvip.top",
    "首页列表数组规则": "body&&.module-items:has(.module-item-pic)",
    "首页片单列表数组规则": ".module-item",
    "首页片单是否Jsoup写法": "1",
    "分类起始页码": "1",
    "分类链接": "http://dmsvip.top/vodshow/{cateId}--{by}------{catePg}---{year}.html",
    "分类名称": "电影&电视剧&综艺&动漫&纪录片",
    "分类名称替换词": "1&2&3&4&29",
    //"筛选数据": {},
    "筛选数据": "ext",
    //{cateId}
    "筛选子分类名称": "空||国产剧&日韩剧&欧美剧",
    "筛选子分类替换词": "空||30&31&32",
    "筛选排序名称": "时间&人气&评分",
    "筛选排序替换词": "time&hits&score",
    "分类截取模式": "1",
    "分类列表数组规则": "body&&.module-item:has(.module-item-text)",
    "分类片单是否Jsoup写法": "是",
    "分类片单标题": ".module-item-title&&Text",
    "分类片单链接": "a&&href",
    "分类片单图片": ".lazyloaded&&data-src",
    "分类片单副标题": ".module-item-text&&Text",
    "分类片单链接加前缀": "http://dmsvip.top",
    "分类片单链接加后缀": "",
    "搜索请求头参数": "User-Agent$MOBILE_UA",
    "搜索链接": "http://dmsvip.top/vodsearch/-------------.html?wd={wd}",
    "POST请求数据": "",
    "搜索截取模式": "1",
    "搜索列表数组规则": ".module-items&&.module-search-item",
    "搜索片单是否Jsoup写法": "是",
    "搜索片单图片": ".lazyload&&data-src",
    "搜索片单标题": "h3&&a&&Text",
    "搜索片单链接": "h3&&a&&href",
    "搜索片单副标题": ".video-info-header&&Text",
    "搜索片单链接加前缀": "http://dmsvip.top",
    "搜索片单链接加后缀": "",
    "链接是否直接播放": "否",
    "直接播放链接加前缀": "https://live.52sf.ga/huya/",
    "直接播放链接加后缀": "#isVideo=true#",
    "直接播放直链视频请求头": "authority$ku.peizq.online#Referer$https://play.peizq.online",
    "详情是否Jsoup写法": "是",
    "类型详情": "",
    "年代详情": "..video-info-main&&.video-info-items,2&&Text",
    "地区详情": "",
    "演员详情": ".video-info-main&&.video-info-items,1&&Text",
    "简介详情": ".video-info-main&&.video-info-items,-1&&Text",
    "线路列表数组规则": ".module-player-tab&&.module-tab-item",
    "线路标题": "Text",
    "播放列表数组规则": "body&&.module-player-list",
    "选集列表数组规则": ".module-tab-content&&a",
    "选集标题链接是否Jsoup写法": "是",
    "选集标题": "a&&Text",
    "选集链接": "a&&href",
    "是否反转选集序列": "否",
    "选集链接加前缀": "http://dmsvip.top",
    "选集链接加后缀": "",
    "分析MacPlayer": "2",
    "是否开启手动嗅探": "否",
    "手动嗅探视频链接关键词": ".mp4#.m3u8#.flv",
    "手动嗅探视频链接过滤词": ".html#=http"
}


站名: 臭蛋蛋

二次截取: class="module-item&&fixedGroup

数组: <a&&no-referrer

分类: 电影&电视剧&综艺&动漫

搜索二次截取: class="module-items&&fixedGroup

搜索数组: <a&&no-referrer

类型: 类型：&&</div>

线路数组: data-dropdown-value=&&</div>

线路标题: <span>&&</small>+集

剧情

n: 全部

n: 喜剧

v: 喜剧

n: 爱情

v: 爱情

n: 恐怖

v: 恐怖

n: 动作

v: 动作

n: 科幻

v: 科幻

n: 剧情

v: 剧情

n: 战争

n: 警匪

v: 警匪

n: 犯罪

n: 动画

n: 奇幻

n: 武侠

n: 冒险

v: 冒险

n: 枪战

v: 枪战

n: 悬疑

v: 悬疑

n: 惊悚

v: 惊悚

n: 经典

v: 经典

n: 青春

v: 青春

n: 文艺

v: 文艺

n: 微电影

v: 微电影

n: 古装

v: 古装

n: 历史

v: 历史

n: 运动

v: 运动

n: 农村

v: 农村

n: 儿童

v: 儿童

n: 网络电影

v: 网络电影

地区

n: 大陆

v: 大陆

n: 香港

v: 香港

n: 台湾

v: 台湾

v: 美国

v: 法国

v: 英国

v: 日本

v: 韩国

v: 德国

v: 泰国

v: 印度

n: 意大利

v: 意大利

n: 西班牙

v: 西班牙

n: 加拿大

v: 加拿大


茶杯狐

[User] 空

houzhui: .html

jiequqian: 空

jiequhou: 空

jiequshuzuqian: class="fed-list-item

tupianqian: data-original="

biaotiqian: .html" >

lianjieqian: href="

sousuohou: 空

sousuohouzhui: 空

jspic: pic

jsname: name

jsid: id

ssjiequqian: 空

ssjiequhou: 空

ssjiequshuzuqian: 空

ssjiequshuzuhou: 空

sstupianqian: 空

sstupianhou: 空

ssbiaotiqian: 空

ssbiaotihou: 空

sslianjieqian: 空

sslianjiehou: 空

bfjiequqian: 空

bfjiequhou: 空

bfjiequshuzuqian: class="fed-part-rows"><li

zhuangtaiqian: 年份：</span>

daoyanqian: 导演：</span>

zhuyanqian: 主演：</span>

juqingqian: 简介：</span>

juqinghou: </div>

bfyjiequqian: 空

bfyjiequhou: 空

bfyjiequshuzuqian: <a

bfbiaotihou: </span>

bflianjieqian: href="


code:
{
    "规则名": "速搜影院",
    "规则作者": "香雅情",
    "请求头参数": "手机",
    "网页编码格式": "UTF-8",
    "图片是否需要代理": "0",
    "是否开启获取首页数据": "1",
    "首页推荐链接": "https://www.sstv.pw",
    "首页列表数组规则": "body&&.list-a",
    "首页片单列表数组规则": "li",
    "首页片单是否Jsoup写法": "1",
    "分类起始页码": "1",
    "分类链接": "https://www.sstv.pw/vodshow-{cateId}/page/{catePg}/?wd=",
    "分类名称": "电影&电视剧&综艺&动漫",
    "分类名称替换词": "1&2&3&4",
    "筛选数据": {},
    //"筛选数据": "ext",
    //{cateId}
    "筛选子分类名称": "",
    "筛选子分类替换词": "",
    //{class}
    "筛选类型名称": "",
    "筛选类型替换词": "*",
    //{area}
    "筛选地区名称": "",
    "筛选地区替换词": "*",
    //{year}
    "筛选年份名称": "",
    "筛选年份替换词": "*",
    //{lang}
    "筛选语言名称": "",
    "筛选语言替换词": "*",
    //{by}
    "筛选排序名称": "时间&人气&评分",
    "筛选排序替换词": "time&hits&score",
    "分类截取模式": "1",
    "分类列表数组规则": ".list-a&&li",
    "分类片单是否Jsoup写法": "1",
    "分类片单标题": ".title&&a&&Text",
    "分类片单链接": ".title&&a&&href[替换:/project-=>||/=>]",
    "分类片单图片": ".lazy&&data-original",
    "分类片单副标题": ".list-remarks&&Text",
    "分类片单链接加前缀": "https://www.sstv.pw/play-",
    "分类片单链接加后缀": "-1-1/",
    "搜索请求头参数": "User-Agent$手机",
    "搜索链接": "https://www.sstv.pw/so/?wd={wd}",
    "POST请求数据": "",
    "搜索截取模式": "1",
    "搜索列表数组规则": "body&&.search-list",
    "搜索片单是否Jsoup写法": "1",
    "搜索片单图片": ".lazy&&data-original",
    "搜索片单标题": "h2&&Text",
    "搜索片单链接": "h2&&a&&href[替换:/project-=>||/=>]",
    "搜索片单副标题": "",
    "搜索片单链接加前缀": "https://www.sstv.pw/play-",
    "搜索片单链接加后缀": "-1-1/",
    "链接是否直接播放": "0",
    "直接播放链接加前缀": "",
    "直接播放链接加后缀": "",
    "直接播放直链视频请求头": "",
    "详情是否Jsoup写法": "1",
    "类型详情": "",
    "年代详情": "",
    "地区详情": "",
    "演员详情": "body&&p:contains(演员：)&&Text!演员：",
    "简介详情": "body&&p:contains(简介：)&&Text!简介：",
    "线路列表数组规则": ".play-source-tab&&a",
    "线路标题": "Text",
    "播放列表数组规则": "body&&.content-playlist",
    "选集列表数组规则": "li",
    "选集标题链接是否Jsoup写法": "1",
    "选集标题": "a&&Text",
    "选集链接": "a&&href",
    "是否反转选集序列": "0",
    "选集链接加前缀": "https://www.sstv.pw",
    "选集链接加后缀": "",
    "分析MacPlayer": "0",
    "是否开启手动嗅探": "0",
    "手动嗅探视频链接关键词": ".mp4#.m3u8#.flv#video/tos",
    "手动嗅探视频链接过滤词": ".html#=http"
}


规则名: 阿里云盘网

搜索列表数组规则: .list&&li

搜索片单图片: img&&src


首页列表数组规则: .module-items

首页片单列表数组规则: .module-item

分类名称: 电影&电视剧&综艺&动漫&纪录片

筛选子分类名称: 动作片&喜剧片&爱情片&恐怖片&犯罪片&科幻片&惊悚片&战争片&悬疑片&灾难片&谍战片&冒险片&剧情片&武侠片&奇幻片&伦理片&动画片&历史片&传记片&歌舞片||国产剧&港台剧&日韩剧&欧美剧&海外剧&泰国剧&其他剧||大陆综艺&港台综艺&日韩综艺&其他综艺||国产动漫&日韩动漫&欧美动漫

筛选地区名称: 大陆&香港&台湾&美国&法国&英国&日本&韩国&德国&泰国&印度&意大利&西班牙&加拿大&其他||内地&韩国&香港&台湾&日本&美国&泰国&英国&新加坡&其他||内地&港台&日韩&欧美||国产&日本&欧美&其他

筛选语言名称: 国语&英语&粤语&闽南语&韩语&日语&法语&德语&其它||国语&英语&粤语&闽南语&韩语&日语&其它||国语&英语&粤语&闽南语&韩语&日语&其它||国语&英语&粤语&闽南语&韩语&日语&其它

筛选排序名称: 时间排序&人气排序&评分排序

分类片单标题: a&&title

分类片单副标题: .module-item-note&&Text

搜索片单标题: .module-card-item-title&&Text

搜索片单副标题: .module-item-note&&Text

简介详情: introduction">&&</p>

手动嗅探视频链接过滤词: .html


code:
{
    "规则名": "98影院",
    "规则作者": "香雅情",
    "请求头参数": "手机",
    "网页编码格式": "UTF-8",
    "图片是否需要代理": "0",
    "是否开启获取首页数据": "1",
    //首页推荐数据获取链接
    "首页推荐链接": "http://www.98dyw.com",
    //首页推荐列表数组截取。
    "首页列表数组规则": "body&&.stui-vodlist:has(h4)",
    //首页推荐片单列表数组定位。
    "首页片单列表数组规则": "li",
    //首页推荐片单信息jsoup与正则截取写法切换，只作用于html网页，1为jsoup写法(默认)，0为正则截取写法
    "首页片单是否Jsoup写法":"1",
    //首页片单标题
    "首页片单标题": "h4&&a&&Text",
    //首页推荐片单链接
    "首页片单链接": "h4&&a&&href",
    //首页推荐片单图片，支持自定义图片链接
    "首页片单图片": ".lazyload&&data-original",
    //首页推荐片单副标题
    "首页片单副标题":".pic-text&&Text",
    //首页推荐片单链接补前缀
    "首页片单链接加前缀": "http://www.98dyw.com",
    //首页推荐片单链接补后缀
    "首页片单链接加后缀": "",

    "分类起始页码": "1",
    "分类链接": "http://www.98dyw.com/{cateId}/index{catePg}.html[firstPage=http://www.98dyw.com/{cateId}/index.html]",
    "分类名称": "电影&电视剧&综艺&动漫",
    "分类名称替换词": "dianyingpian&dianshiju&zongyi&dongman",
    "筛选数据": {},
    "分类截取模式": "1",
    "分类列表数组规则": ".stui-vodlist&&li",
    "分类片单是否Jsoup写法": "1",
    "分类片单标题": "h4&&a&&Text",
    "分类片单链接": "h4&&a&&href",
    "分类片单图片": ".lazyload&&data-original",
    "分类片单副标题": ".pic-text&&Text",
    "分类片单链接加前缀": "http://www.98dyw.com",
    "分类片单链接加后缀": "",
    "搜索请求头参数": "User-Agent$手机",
    "搜索链接": "http://www.98dyw.com/search.php;post",
    "POST请求数据": "searchword={wd}",
    "搜索截取模式": "1",
    "搜索列表数组规则": ".stui-vodlist__media&&li",
    "搜索片单是否Jsoup写法": "1",
    "搜索片单图片": ".lazyload&&data-original",
    "搜索片单标题": "h3&&a&&Text",
    "搜索片单链接": "h3&&a&&href",
    "搜索片单副标题": ".pic-text&&Text",
    "搜索片单链接加前缀": "http://www.98dyw.com",
    "搜索片单链接加后缀": "",
    "链接是否直接播放": "0",
    "直接播放链接加前缀": "https://live.52sf.ga/huya/",
    "直接播放链接加后缀": "#isVideo=true#",
    "直接播放直链视频请求头": "authority$ku.peizq.online#Referer$https://play.peizq.online",
    "详情是否Jsoup写法": "0",
    "类型详情": "类型：&&</a",
    "年代详情": "",
    "地区详情": "",
    "演员详情": "主演：&&
",
    "简介详情": "简介：&&
",
    "线路列表数组规则": "body&&#playlist",
    "线路标题": "h3&&Text",
    "播放列表数组规则": "body&&.stui-content__playlist",
    "选集列表数组规则": "li",
    "选集标题链接是否Jsoup写法": "1",
    "选集标题": "a&&Text",
    "选集链接": "a&&href",
    "是否反转选集序列": "0",
    "选集链接加前缀": "http://www.98dyw.com",
    "选集链接加后缀": "",
    "分析MacPlayer": "0",
    "是否开启手动嗅探": "1",
    "手动嗅探视频链接关键词": ".mp4#.m3u8#.flv#pt=m3u8#video/tos",
    "手动嗅探视频链接过滤词": ".html#=http"
}


作者: 蓝魔魔

站名: WZ影视

数组: <a &&class="icon-play

副标题: class="module-item-note">&&</div>

影片类型: 类型：&&</li>

影片状态: 状态：&&</li>

导演: 导演：&&</li>

主演: 主演：&&</li>

线路数组: class="module-tab-item tab-item&&</div>

播放数组: class="module-play-list-content&&</div>

搜索数组: <a &&class="icon-play

搜索副标题: class="module-item-note">&&</div>

类型

n: 全部

n: 动作片

n: 喜剧片

n: 爱情片

n: 科幻片

n: 恐怖片

n: 剧情片

n: 战争片

剧情

n: 喜剧

v: 喜剧

n: 爱情

v: 爱情

n: 恐怖

v: 恐怖

n: 动作

v: 动作

n: 科幻

v: 科幻

n: 剧情

v: 剧情

n: 战争

v: 战争

n: 警匪

v: 警匪

n: 犯罪

v: 犯罪

n: 动画

v: 动画

n: 奇幻

v: 奇幻

n: 武侠

v: 武侠

n: 冒险

v: 冒险

n: 枪战

v: 枪战

n: 悬疑

v: 悬疑

n: 惊悚

v: 惊悚

n: 灾难

v: 灾难

n: 西部

v: 西部

n: 魔幻

v: 魔幻

v: 古装

n: 传记

v: 传记

v: 历史

n: 纪录

v: 纪录

v: 经典

v: 青春

v: 文艺

n: 歌舞

v: 歌舞

v: 运动

n: 情色

v: 情色

n: 伦理

v: 伦理

地区

n: 内地

v: 内地


code:
        {"key": "csp_XYQHiker_兔小贝","name":"兔小贝(XYQH)","type":3,"api":"csp_XYQHiker","searchable":1,"quickSearch":1,"filterable":1,"ext":"./XYQHiker/兔小贝.json"},
        {"key": "csp_XYQHiker_兔小贝2","name":"兔小贝2(XYQH)","type":3,"api":"csp_XYQHiker","searchable":1,"quickSearch":1,"filterable":1,"ext":"./XYQHiker/兔小贝2.json"},
        {"key": "csp_XYQHiker_播视童趣","name":"播视童趣(XYQH)","type":3,"api":"csp_XYQHiker","searchable":1,"quickSearch":1,"filterable":1,"ext":"./XYQHiker/播视童趣.json"},
        {"key": "csp_XYQHiker_有声小说吧","name":"有声小说吧(XYQH)","type":3,"api":"csp_XYQHiker","searchable":1,"quickSearch":1,"filterable":1,"ext":"./XYQHiker/有声小说吧.json"},
        {"key": "csp_XYQHiker_DJ呦呦音乐网","name":"🎧DJ呦呦音乐网(XYQH)","type":3,"api":"csp_XYQHiker","searchable":1,"quickSearch":1,"filterable":1,"ext":"./XYQHiker/DJ呦呦音乐网.json"},
        {"key": "csp_XYQHiker_MV263","name":"MV263(XYQH)","type":3,"api":"csp_XYQHiker","searchable":0,"quickSearch":0,"filterable":1,"ext":"./XYQHiker/MV263.json"},
        {"key": "csp_XYQHiker_酷奇MV","name":"酷奇MV(XYQH)","type":3,"api":"csp_XYQHiker","searchable":0,"quickSearch":0,"filterable":1,"ext":"./XYQHiker/酷奇MV.json"},
        //{"key": "csp_XYQHiker_柠檬TV","name":"柠檬TV(XYQH)","type":3,"api":"csp_XYQHiker","searchable":1,"quickSearch":1,"filterable":1,"ext":"./XYQHiker/柠檬TV.json"},
        {"key": "csp_XYQHiker_柠檬TV2","name":"柠檬TV2(XYQH)","type":3,"api":"csp_XYQHiker","searchable":1,"quickSearch":1,"filterable":1,"ext":"./XYQHiker/柠檬TV2.json"},

        {"key": "csp_XYQHiker_野荷塘影院","name":"野荷塘影院(XYQH)","type":3,"api":"csp_XYQHiker","searchable":1,"quickSearch":1,"filterable":1,"ext":"./XYQHiker/野荷塘影院.json"},
        {"key": "csp_XYQHiker_肥猫影视","name":"肥猫影视(XYQH)","type":3,"api":"csp_XYQHiker","searchable":1,"quickSearch":1,"filterable":1,"ext":"./XYQHiker/肥猫影视.json"},
        {"key": "csp_XYQHiker_红领巾影院","name":"红领巾影院(XYQH)","type":3,"api":"csp_XYQHiker","searchable":1,"quickSearch":1,"filterable":1,"ext":"./XYQHiker/红领巾影院.json"},
        {"key": "csp_XYQHiker_星辰影院","name":"⭐星辰影院(XYQH)","type":3,"api":"csp_XYQHiker","searchable":1,"quickSearch":1,"filterable":1,"ext":"./XYQHiker/星辰影院.json"},
        {"key": "csp_XYQHiker_小猫咪","name":"小猫咪(XYQH)","type":3,"api":"csp_XYQHiker","searchable":1,"quickSearch":1,"filterable":1,"ext":"./XYQHiker/小猫咪.json"},
        {"key": "csp_XYQHiker_188影视","name":"188影视(XYQH)","type":3,"api":"csp_XYQHiker","searchable":1,"quickSearch":1,"filterable":1,"ext":"./XYQHiker/188影视.json"},
        {"key": "csp_XYQHiker_55影

path: json/yinsgs


code:
{
    "规则名": "子子影视",
    "规则作者": "香雅情",
    "请求头参数": "MOBILE_UA",
    "网页编码格式": "UTF-8",
    "是否开启获取首页数据":"1",
    "图片是否需要代理": "否",
    "是否开启获取首页数据": "1",
    "首页推荐链接": "https://www.ziziys.com",
    "首页列表数组规则": "body&&.module-items:has(.module-item-pic)",
    "首页片单列表数组规则": ".module-item",
    "首页片单是否Jsoup写法": "1",
    "分类起始页码": "1",
    "分类链接": "https://www.ziziys.com/list/{cateId}-{catePg}.html",
    "分类名称": "电影&电视剧&动漫",
    "分类名称替换词": "1&2&3",
    //"筛选数据": {},
    "筛选数据": "ext",
    //{cateId}
    "筛选子分类名称": "动漫片&恐怖片&历史传记片&战争片&情色片&武侠古装&记录片&灾难片&音乐歌舞&运动片&科幻片&动作片&剧情片&喜剧&爱情片&惊悚片&奇幻片&悬疑片||国产剧&美剧&日韩剧&泰剧||国漫&日漫&欧美动漫",
    "筛选子分类替换词": "23&24&25&26&27&28&29&30&31&32&33&6&7&8&9&10&11&12||13&14&15&16||20&21&22",
    "分类截取模式": "1",
    "分类列表数组规则": "body&&.module-item:has(.module-item-text)",
    "分类片单是否Jsoup写法": "是",
    "分类片单标题": ".module-item-title&&Text",
    "分类片单链接": "a&&href",
    "分类片单图片": ".lazyloaded&&data-src",
    "分类片单副标题": ".module-item-text&&Text",
    "分类片单链接加前缀": "https://www.ziziys.com",
    "分类片单链接加后缀": "",
    "搜索请求头参数": "User-Agent$MOBILE_UA",
    "搜索链接": "https://www.ziziys.com/vsearch/--.html?wd={wd}",
    "POST请求数据": "wd={wd}&search=",
    "搜索截取模式": "1",
    "搜索列表数组规则": ".module-items&&.module-search-item",
    "搜索片单是否Jsoup写法": "是",
    "搜索片单图片": ".lazyload&&data-src",
    "搜索片单标题": "h3&&a&&Text",
    "搜索片单链接": "h3&&a&&href",
    "搜索片单副标题": ".video-info-header&&Text",
    "搜索片单链接加前缀": "https://www.ziziys.com",
    "搜索片单链接加后缀": "",
    "链接是否直接播放": "否",
    "直接播放链接加前缀": "https://live.52sf.ga/huya/",
    "直接播放链接加后缀": "#isVideo=true#",
    "直接播放直链视频请求头": "authority$ku.peizq.online#Referer$https://play.peizq.online",
    "详情是否Jsoup写法": "是",
    "类型详情": "",
    "年代详情": "..video-info-main&&.video-info-items,2&&Text",
    "地区详情": "",
    "演员详情": ".video-info-main&&.video-info-items,1&&Text",
    "简介详情": ".video-info-main&&.video-info-items,-1&&Text",
    "线路列表数组规则": ".module-player-tab&&.module-tab-item",
    "线路标题": "Text",
    "播放列表数组规则": "body&&.module-player-list",
    "选集列表数组规则": ".module-tab-content&&a",
    "选集标题链接是否Jsoup写法": "是",
    "选集标题": "a&&Text",
    "选集链接": "a&&href",
    "是否反转选集序列": "否",
    "选集链接加前缀": "https://www.ziziys.com",
    "选集链接加后缀": "",
    "分析MacPlayer": "是",
    "是否开启手动嗅探": "否",
    "手动嗅探视频链接关键词": ".mp4#.m3u8#.flv",
    "手动嗅探视频链接过滤词": ".html#=http"
}


code:
# Extensions and prefixes.
*.tmp
tmp.*
*~
?*.gitignore
gitignore*

# Specific files.
/data
/gem5/out
/images-*.zip
/out
/out.*

# Python trash.
*.pyc
__pycache__

repo_name: liberatehongkong/linux

path: .gitignore

license: mit


code:
[submodule "submodules/buildroot"]
	path = submodules/buildroot
	url = https://github.com/cirosantilli/buildroot
	ignore = dirty
[submodule "submodules/qemu"]
	path = submodules/qemu
	url = https://github.com/cirosantilli/qemu
# The true upstream does not accept git submodule update --init --depth 1
# git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git
# But git clone --branch --depth 1 worked weirdly:
# https://unix.stackexchange.com/questions/338578/linux-kernel-source-code-size-difference
[submodule "submodules/linux"]
	path = submodules/linux
	url = https://github.com/cirosantilli/linux
[submodule "submodules/gem5"]
	path = submodules/gem5
	url = https://gem5.googlesource.com/public/gem5
[submodule "submodules/parsec-benchmark"]
	path = submodules/parsec-benchmark
	url = https://github.com/cirosantilli/parsec-benchmark

path: .gitmodules


code:
" Use this automatically with:
"
" Plugin 'embear/vim-localvimrc'
" let g:localvimrc_ask = 0
" let g:localvimrc_sandbox = 0

if &filetype ==# 'c' || &filetype ==# 'cpp'
  setlocal noexpandtab
endif

path: .lvimrc


code:
language: cpp

sudo: required

install: |
  cd "$TRAVIS_BUILD_DIR"
  bash -x ./configure -t

script: |
  cd "$TRAVIS_BUILD_DIR"
  # -j: I'm unable to install nproc on Travis.
  # TODO why? Is part of coreutils in Ubuntu 16.04:
  # http://manpages.ubuntu.com/manpages/trusty/man1/nproc.1.html
  # which ./configure is installing.
  #
  # awk: without it, too much stdout (4Mb max)
  # If we ignore stdout: Travis kills job because it spent
  # too long without any new stdout.
  #
  # -S otherwise apt-get instlal fails with:
  # The following packages have unmet dependencies:
  # libsdl2-dev : Depends: libegl1-mesa-dev
  #            Depends: libgles2-mesa-dev
  bash -x ./build-qemu -j 16 -S |& awk 'NR % 1000 == 0'
  bash -x ./build -j 16 -S |& awk 'NR % 1000 == 0'
  bash -x ./run -e 'init=/poweroff.out'


code:
= CONTRIBUTING

See: link:README.adoc#contributing[]


code:
#!/usr/bin/env bash
set -eux
root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
bench_all=false
bench_buildroot_build=false
bench_buildroot_baseline_build=false
bench_gem5_build=false
bench_linux_boot=false
default_arch=x86_64
update_repo=false
while getopts Aa:Bbglu OPT; do
  case "$OPT" in
    A)
      bench_all=true
      ;;
    a)
      default_arch="$OPTARG"
      ;;
    b)
      bench_buildroot_build=true
      ;;
    B)
      bench_buildroot_baseline_build=true
      ;;
    g)
      bench_gem5_build=true
      ;;
    l)
      bench_linux_boot=true
      ;;
    u)
      update_repo=true
      ;;
    ?)
      exit 2
      ;;
  esac
done
shift "$(($OPTIND - 1))"
comment="${1:-}"
if \
  ! "$bench_buildroot_build" && \
  ! "$bench_buildroot_baseline_build" && \
  ! "$bench_gem5_build" && \
  ! "$bench_linux_boot" \
; then
  bench_all=true
fi
if "$bench_all"; then
  bench_buildroot_build=true
  bench_buildroot_baseline_build=true
  bench_gem5_build=true
  bench_linux_boot=true
fi
getvar="${root_dir}/getvar"

# Create output directory.
benchmark_repo="${root_dir}/../linux-kernel-module-cheat-regression"
mkdir -p "$benchmark_repo"
last_dir="$(ls "$benchmark_repo" | grep -E '^[0-9]' | tail -n 1)"
if [ -n "$last_dir" ]; then
  seq_id="$(("$(echo "$last_dir" | sed -E -e 's/_.*//' -e 's/^0*//')" + 1))"
else
  seq_id=0
fi
seq_id="$(printf '%0.4d' "$seq_id")"
dir_basename="${seq_id}_$("$getvar" sha)"
new_dir="${benchmark_repo}/${dir_basename}"
mkdir "$new_dir"

do_bench_buildroot_build() (
  arch="$default_arch"
  build_id=bench
  if [ "$1" = baseline ]; then
    baseline=--baseline
    baseline_suffix=-baseline
  else
    baseline=
    baseline_suffix=
  fi
  common_build_dir="$("$getvar" --arch "$arch" --buildroot-build-id "$build_id" build_dir)"
  common_images_dir="$("$getvar" --arch "$arch" --buildroot-build-id "$build_id" images_dir)"
  "${root_dir}/build-buildroot" --arch "$arch" $baseline --buildroot-build-id "$build_id" --clean
  "${root_dir}/build-buildroot" --arch "$arch" $baseline --buildroot-build-id "$build_id" --no-all -- source
  "${root_dir}/build-buildroot" --arch "$arch" $baseline --buildroot-build-id "$build_id"
  cp "${common_build_dir}/build-time.log" "${new_dir}/buildroot-build-time-${baseline_suffix}${arch}.log"
  wc -c "${images_dir}/"* > "${new_dir}/buildroot-image-size-${baseline_suffix}${arch}.log"
  "${root_dir}/build-buildroot" --arch "$arch" $baseline --buildroot-build-id "$build_id" --clean
)

if "$bench_buildroot_build"; then
  do_bench_buildroot_build
fi

if "$bench_buildroot_baseline_build"; then
  do_bench_buildroot_build baseline
fi

if "$bench_gem5_build"; then
  common_arch="$default_arch"
  gem5_build_id=bench-build
  common_gem5_build_dir="$("$getvar" --arch "$common_arch" --gem5-build-id "$gem5_build_id" gem5_out_dir)"
  common_gem5_src_dir="$("$getvar" --arch "$common_arch" --gem5-build-id "$gem5_build_id" gem5_src_dir)"
  results_file="${common_gem5_build_dir}/bench-build.txt"
  git -C "${common_gem5_src_dir}" clean -xdf
  rm -f "$results_file"
  "${root_dir}/build-gem5" --arch "$common_arch" --clean --gem5-build-id "$gem5_build_id"
  # TODO understand better: --foreground required otherwise we cannot
  # kill the build with Ctrl+C if something goes wrong, can be minimized to:
  # bash -c "eval 'timeout 5 sleep 3'"
  "${root_dir}/bench-cmd" "timeout --foreground 900 ./build-gem5 --arch '$common_arch' --gem5-build-id '$gem5_build_id'" "$results_file"
  cp "$results_file" "${new_dir}/gem5-bench-build-${common_arch}.txt"
  git -C "${common_gem5_src_dir}" clean -xdf
  "${root_dir}/build-gem5" --arch "$common_arch" --clean --gem5-build-id "$gem5_build_id"
fi

if "$bench_linux_boot"; then
  cd "${root_dir}"
  "${root_dir}/build-all"
  "${root_dir}/bench-boot" -t 3
  cp "$(${root_dir}/getvar bench_boot)" "$new_dir"
fi

if "$update_repo"; then
  if [ -n "$comment" ]; then
    echo "$comment" > "${new_dir}/README.adoc"
  fi
  echo
  cd "$benchmark_repo"
  git add .
  git commit -m "$dir_basename"
  git push
fi

path: bench-all


code:
#!/usr/bin/env bash
set -eu
root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
test_size=1
OPTIND=1
while getopts t: OPT; do
  case "$OPT" in
    t)
      # 1: a few seconds and important
      # 2: < 5 minutes and important or a few seconds and not too important
      # 3: all
      test_size="$OPTARG"
      ;;
    ?)
      exit 2
      ;;
  esac
done
shift "$(($OPTIND - 1))"
if [ $# -gt 1 ]; then
  extra_args=" $*"
else
  extra_args=
fi
getvar="${root_dir}/getvar"
common_bench_boot="$("$getvar" bench_boot)"
caches='--caches --l2cache --l1d_size=1024kB --l1i_size=1024kB --l2_size=1024kB --l3_size=1024kB'

bench() (
  "${root_dir}/bench-cmd" "./run --arch ${1}${extra_args}" "$common_bench_boot"
)

gem5_insts() (
  printf "instructions $(./gem5-stat --arch "$1" sim_insts)\n" >> "$common_bench_boot"
)

qemu_insts() (
  common_arch="$1"
  ./qemu-trace2txt --arch "$common_arch"
  common_qemu_trace_txt_file="$("$getvar" --arch "$common_arch" qemu_trace_txt_file)"
  printf "instructions $(wc -l "${common_qemu_trace_txt_file}" | cut -d' ' -f1)\n" >> "$common_bench_boot"
)

newline() (
  echo >> "$common_bench_boot"
)

rm -f "${common_bench_boot}"

arch=x86_64
bench "${arch} --eval '/poweroff.out'"
newline
bench "${arch} --eval '/poweroff.out' --kvm"
newline
if [ "$test_size" -ge 2 ]; then
  bench "${arch} --eval '/poweroff.out' --trace exec_tb"
  qemu_insts "$arch"
  newline
  bench "$arch --eval 'm5 exit' --gem5"
  gem5_insts "$arch"
  newline
fi
#bench "$arch --eval 'm5 exit' --gem5 -- --cpu-type=DerivO3CPU ${caches}"
#gem5_insts "$arch"
#newline

arch=arm
bench "$arch --eval '/poweroff.out'"
newline
if [ "$test_size" -ge 2 ]; then
  bench "$arch --eval '/poweroff.out' --trace exec_tb"
  qemu_insts "$arch"
  newline
  #bench "$arch --eval 'm5 exit' --gem5"
  #gem5_insts "$arch"
  #newline
fi
#if [ "$test_size" -ge 3 ]; then
#  bench "$arch --eval 'm5 exit' --gem5 -- --cpu-type=HPI ${caches}"
#  gem5_insts "$arch"
#  newline
#fi

arch=aarch64
bench "$arch --eval '/poweroff.out'"
newline
if [ "$test_size" -ge 2 ]; then
  bench "$arch --eval '/poweroff.out' --trace exec_tb"
  qemu_insts "$arch"
  newline
  bench "$arch --eval 'm5 exit' --gem5"
  gem5_insts "$arch"
  newline
fi
if [ "$test_size" -ge 3 ]; then
  bench "$arch --eval 'm5 exit' --gem5 -- --cpu-type=HPI ${caches}"
  gem5_insts "$arch"
  newline
fi

path: bench-boot


code:
#!/usr/bin/env bash
# Benchmark a command as a string and output results
# to a file with format:
#
# cmd <command run>
# time <time in seconds to finish>
# exit_status <exit status>
set -eu
root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
# Command to benchmark
cmd="$1"
shift
# Where to append write results to. Default: /dev/null.
results_file="${1:-/dev/null}"
printf 'cmd ' >> "$results_file"
env time --append -f 'time %e' --output="$results_file" "${root_dir}/eeval" -a "$cmd" "$results_file"
printf "exit_status $?\n" >> "$results_file"

path: bench-cmd


code:
#!/usr/bin/env python3

import imp
import os
import shutil
import sys

import common
build = imp.load_source('build', os.path.join(common.root_dir, 'build'))
run = imp.load_source('run', os.path.join(common.root_dir, 'run'))

parser = common.get_argparse(
    argparse_args={
        'description': '''Bisect the Linux kernel on gem5 boots.

More information at: https://github.com/cirosantilli/linux-kernel-module-cheat#bisection
'''},
    default_args={
        'gem5': True,
        'linux_build_id': 'bisect',
    },
)
args = common.setup(parser)
# We need a clean rebuild because rebuilds at different revisions:
# - may fail
# - may not actually rebuild all files, e.g. on header changes
common.rmrf(common.linux_variant_dir)
assert build.main(args) == 0
status = run.main(args, {
    'eval': 'm5 exit',
})
if status == 125 or status == 127:
    status = 1
sys.exit(status)


code:
#!/usr/bin/env bash
set -eu
git submodule update
cd ..
./build-qemu --arch arm -Q bisect
./run --arch arm -E '/poweroff.out' -Q bisect

path: bisect-qemu-linux-boot


code:
#!/usr/bin/env python3
import common
parser = common.get_argparse(
    argparse_args={'description':'Convert a BST vs heap stat file into a gnuplot input'}
)
args = common.setup(parser)
stats = common.get_stats()
it = iter(stats)
i = 1
for stat in it:
    try:
        next_stat = next(it)
    except StopIteration:
        # Automatic dumpstats at end may lead to odd number of stats.
        break
    print('{} {} {}'.format(i, stat, next_stat))
    i += 1

path: bst-vs-heap


code:
#!/usr/bin/env bash
set -eu
archs='x86_64 arm aarch64'
gem5=true
while getopts A:G OPT; do
  case "$OPT" in
    A)
      archs="$OPTARG"
      ;;
    G)
      gem5=false
      ;;
  esac
done
shift "$(($OPTIND - 1))"
for arch in $archs; do
  ./build-qemu --arch "$arch"
  ./build --arch "$arch" --kernel-modules -l "$@"
  if "$gem5"; then
    ./build-gem5 --arch "$arch"
  fi
done

path: build-all


code:
#!/usr/bin/env python3

import multiprocessing
import os
import pathlib
import shutil
import subprocess
import sys
import re

import common

defaults = {
    'baseline': False,
    'buildroot_bare_kernel': False,
    'buildroot_config': [],
    'buildroot_config_fragment': [],
    'initramfs': False,
    'initrd': False,
    'kernel_config': [],
    'kernel_config_fragment': [],
    'kernel_custom_config_file': None,
    'kernel_modules': False,
    'linux_reconfigure': False,
    'no_all': False,
    'nproc': None,
    'skip_configure': False,
    'verbose': False,
    'extra_make_args': [],
}

def get_argparse():
    parser = common.get_argparse(argparse_args={'description':'Run Linux on an emulator'})
    common.add_build_arguments(parser)
    parser.add_argument(
        '-B', '--buildroot-config', default=defaults['buildroot_config'], action='append',
        help='''Add a single Buildroot config to the current build.
Example value: 'BR2_TARGET_ROOTFS_EXT2_SIZE="512M"'.
Can be used multiple times to add multiple configs.
Takes precedence over any Buildroot config files.
'''
    )
    parser.add_argument(
        '-b', '--buildroot-config-fragment', default=defaults['buildroot_config_fragment'], action='append',
        help='''Also use the given Buildroot configuration fragment file.
Pass multiple times to use multiple fragment files.'''
    )
    parser.add_argument(
        '--baseline', default=defaults['baseline'], action='store_true',
        help='''Do a default-ish Buildroot defconfig build, without any of our extra options.
Mostly to track how much slower we are than a basic build.'''
    )
    parser.add_argument(
        '-C', '--kernel-config', default=defaults['kernel_config'], action='append',
        help='''Add a single kernel config configs to the current build.
Example value: 'CONFIG_FORTIFY_SOURCE=y'.
Can be used multiple times to add multiple configs.
Takes precedence over any Buildroot config files.
'''
    )
    parser.add_argument(
        '-c', '--kernel-config-fragment', default=defaults['kernel_config_fragment'], action='append',
        help='''Also use the given kernel configuration fragment file.
Pass multiple times to use multiple fragment files.'''
    )
    parser.add_argument(
        '-I', '--initramfs', default=defaults['initramfs'], action='store_true',
    )
    parser.add_argument(
        '-i', '--initrd', default=defaults['initrd'], action='store_true',
    )
    parser.add_argument(
        '-j', '--nproc', default=defaults['nproc'], type=int,
        help='Number of processors to use for the build. Default: all.'
    )
    parser.add_argument(
        '-K', '--kernel-custom-config-file', default=defaults['kernel_custom_config_file'],
        help='''Ignore all default kernel configurations and use this file instead.
Still uses options explicitly passed with `-C` and `-c` on top of it.'''
    )
    parser.add_argument(
        '-k', '--kernel-modules', default=defaults['kernel_modules'], action='store_true',
        help='Reconfigure and rebuild the kernel modules'
    )
    parser.add_argument(
        '-l', '--linux-reconfigure', default=defaults['linux_reconfigure'], action='store_true',
        help='''Reconfigure and rebuild the Linux kernel.
Touches kernel configuration files to overcome:
https://stackoverflow.com/questions/49260466/why-when-i-change-br2-linux-kernel-custom-config-file-and-run-make-linux-reconfi'''
    )
    parser.add_argument(
        '--no-all', default=defaults['no_all'], action='store_true',
        help='''Don't build the all target which normally gets build by default.
That target builds the root filesystem and all its dependencies.'''
    )
    parser.add_argument(
        '--skip-configure', default=defaults['skip_configure'], action='store_true',
        help='''Skip the Buildroot configuration. Saves a few seconds,
but requires you to know what you are doing :-)'''
    )
    parser.add_argument(
        '-v', '--verbose', default=defaults['verbose'], action='store_true',
        help='Do a verbose build'
    )
    parser.add_argument(
        'extra_make_args', default=defaults['extra_make_args'], metavar='extra-make-args', nargs='*',
        help='''Extra arguments to be passed to the Buildroot make,
usually extra Buildroot targets.'''
    )
    return parser

def main(args, extra_args=None):
    global defaults
    args = common.resolve_args(defaults, args, extra_args)
    if args.clean:
        common.rmrf(common.buildroot_build_dir)
    else:
        os.makedirs(common.out_dir, exist_ok=True)
        extra_make_args = args.extra_make_args.copy()
        if args.kernel_modules:
            extra_make_args.append('kernel_modules-reconfigure')
        if args.linux_reconfigure:
            extra_make_args.append('linux-reconfigure')
        if args.gem5:
            extra_make_args.append('gem5-reconfigure')
        if args.nproc is None:
            nproc = multiprocessing.cpu_count()
        else:
            nproc = args.nproc
        if args.arch == 'x86_64':
            defconfig = 'qemu_x86_64_defconfig'
        elif args.arch == 'arm':
            defconfig = 'qemu_arm_vexpress_defconfig'
        elif args.arch == 'aarch64':
            defconfig = 'qemu_aarch64_virt_defconfig'

        # Configure.
        if not args.skip_configure:
            # Initial make configure.
            # TODO port and test.
            #cd "${common_buildroot_src_dir}"
            #for p in $(find "${common_root_dir}/patches/buildroot/" -maxdepth 1 -name '*.patch' -print); do
            #  patch -N -r - -p 1 < "$p" || :
            #done
            br2_external_dirs = []
            packages_dir = os.path.join(common.root_dir, 'packages')
            for package_dir in os.listdir(packages_dir):
                package_dir_abs = os.path.join(packages_dir, package_dir)
                if os.path.isdir(package_dir_abs):
                    br2_external_dirs.append(path_relative_to_buildroot(package_dir_abs))
            br2_external_str = ':'.join(br2_external_dirs)
            subprocess.check_call(
                [
                    'make',
                    'O={}'.format(common.buildroot_build_dir),
                    'BR2_EXTERNAL={}'.format(br2_external_str),
                    defconfig,
                ],
                cwd=common.buildroot_src_dir,
            )
            buildroot_configs = args.buildroot_config
            buildroot_configs.extend([
                'BR2_JLEVEL={}'.format(nproc),
                'BR2_DL_DIR="{}"'.format(common.dl_dir),
            ])
            write_configs(buildroot_configs)
            if not args.baseline:
                buildroot_configs.extend([
                    'BR2_GLOBAL_PATCH_DIR="{}"'.format(
                        path_relative_to_buildroot(os.path.join(common.root_dir, 'patches', 'global'))),
                    'BR2_PACKAGE_BUSYBOX_CONFIG_FRAGMENT_FILES="{}"'.format(
                        path_relative_to_buildroot(os.path.join(common.root_dir, 'busybox_config_fragment'))),
                    'BR2_PACKAGE_OVERRIDE_FILE="{}"'.format(
                        path_relative_to_buildroot(os.path.join(common.root_dir, 'buildroot_override'))),
                    'BR2_ROOTFS_OVERLAY="{}"'.format(
                        path_relative_to_buildroot(os.path.join(common.root_dir, 'rootfs_overlay'))),
                    'BR2_ROOTFS_POST_BUILD_SCRIPT="{}"'.format(
                        path_relative_to_buildroot(os.path.join(common.root_dir, 'rootfs_post_build_script'))),
                    'BR2_ROOTFS_USERS_TABLES="{}"'.format(
                        path_relative_to_buildroot(os.path.join(common.root_dir, 'user_table'))),
                ])
                if args.gem5:
                    buildroot_configs.append('BR2_PACKAGE_GEM5=y')
                if args.initramfs:
                    buildroot_configs.extend([
                        'BR2_TARGET_ROOTFS_CPIO=n',
                        'BR2_TARGET_ROOTFS_EXT2=n',
                        'BR2_TARGET_ROOTFS_INITRAMFS=y',
                    ])
                if args.initrd:
                    buildroot_configs.extend([
                        'BR2_TARGET_ROOTFS_CPIO=y',
                        'BR2_TARGET_ROOTFS_EXT2=n'
                        'BR2_TARGET_ROOTFS_INITRAMFS=n',
                    ])
                buildroot_config_fragments = [
                    os.path.join(common.root_dir, 'buildroot_config', 'default')
                ] + args.buildroot_config_fragment

                # Decide kernel configuration.
                kernel_config_fragments = []
                if True:
                    # CLI kernel configurations.
                    kernel_config_fragment_cli_path = os.path.join(common.buildroot_build_dir, 'lkmc_kernel_config_fragment_cli')
                    kernel_config_cli_str = '\n'.join(args.kernel_config)
                    do_write = False
                    if os.path.exists(kernel_config_fragment_cli_path):
                        with open(kernel_config_fragment_cli_path, 'r') as kernel_config_fragment_cli_file:
                            kernel_config_cli_str_old = kernel_config_fragment_cli_file.read()
                            if kernel_config_cli_str != kernel_config_cli_str_old:
                                do_write = True
                    else:
                        do_write = True
                    if do_write:
                        #  Only update if modified, otherwise Buildroot tries to
                        # rebuilds the kernel every time, which takes a few seconds.
                        # even when the kernel has already been built.
                        with open(kernel_config_fragment_cli_path, 'w') as kernel_config_fragment_cli_file:
                            kernel_config_fragment_cli_file.write(kernel_config_cli_str)
                    kernel_config_fragments.append(os.path.join(kernel_config_fragment_cli_path))
                if True:
                    # Kernel configuration fragments.
                    if args.kernel_custom_config_file is not None:
                        if os.path.exists(args.kernel_custom_config_file):
                            buildroot_configs.extend([
                                'BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y',
                                'BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE=\"{}\"'.format(args.kernel_custom_config_file),
                            ])
                            if args.linux_reconfigure:
                                pathlib.Path(args.kernel_custom_config_file).touch()
                        else:
                            raise Exception('Kernel config fragment file does not exist: {}'.format(args.kernel_custom_config_file))
                        default_kernel_config_fragments = []
                    else:
                        kernel_config_fragment_dir = os.path.join(common.root_dir, 'kernel_config')
                        default_kernel_config_fragments = ['min', 'default']
                        if args.linux_reconfigure:
                            # https://stackoverflow.com/questions/49260466/why-when-i-change-br2-linux-kernel-custom-config-file-and-run-make-linux-reconfi
                            pathlib.Path(os.path.join(kernel_config_fragment_dir, 'min')).touch()
                    for i, default_kernel_config_fragment in enumerate(default_kernel_config_fragments):
                        default_kernel_config_fragments[i] = os.path.join(kernel_config_fragment_dir, default_kernel_config_fragment)
                kernel_config_fragments.extend(default_kernel_config_fragments)
                for i, frag in enumerate(kernel_config_fragments):
                    kernel_config_fragments[i] = path_relative_to_buildroot(frag)
                buildroot_kernel_config_fragment_str = 'BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="{}"'.format(' '.join(kernel_config_fragments))
                buildroot_configs.append(buildroot_kernel_config_fragment_str)
                write_configs(buildroot_configs, buildroot_config_fragments)
                subprocess.check_call(
                    [
                        'make',
                        'O={}'.format(common.buildroot_build_dir),
                        'olddefconfig',
                    ],
                    cwd=common.buildroot_src_dir,
                )

        # Manage Linux kernel and QEMU variants.
        def symlink_buildroot_variant(custom_dir, variant_dir):
            if os.path.islink(custom_dir):
                os.unlink(custom_dir)
            elif os.path.isdir(custom_dir):
                # Migration for existing builds.
                shutil.move(custom_dir, variant_dir)
            os.makedirs(variant_dir, exist_ok=True)
            os.symlink(variant_dir, custom_dir)
        symlink_buildroot_variant(common.linux_build_dir, common.linux_variant_dir)

        # Do the actual build.
        common.mkdir()
        if not args.no_all:
            extra_make_args.append('all')
        assert common.run_cmd(
            [
                'make',
                'LKMC_GEM5_SRCDIR="{}"'.format(common.gem5_src_dir),
                'LKMC_PARSEC_BENCHMARK_SRCDIR="{}"'.format(common.parsec_benchmark_src_dir),
                'O={}'.format(common.buildroot_build_dir),
                'V={}'.format(int(args.verbose)),
            ] +
            extra_make_args
            ,
            out_file=os.path.join(common.buildroot_build_dir, 'lkmc.log'),
            delete_env=['LD_LIBRARY_PATH'],
            cwd=common.buildroot_src_dir,
        ) == 0

        # Create the qcow2 from ext2. This is optional, because gem5
        # does not need the qcow2.
        if os.path.exists(common.qemu_img_executable) and os.path.exists(common.ext2_file):
            assert common.run_cmd([
                common.qemu_img_executable,
                '-T', 'pr_manager_run,file=/dev/null',
                'convert',
                '-f', 'raw',
                '-O', 'qcow2',
                common.ext2_file,
                common.qcow2_file,
            ]) == 0

        return 0

def path_relative_to_buildroot(abspath):
    return os.path.relpath(abspath, common.buildroot_src_dir)

def write_configs(buildroot_configs, buildroot_config_fragments=None):
    """
    Write extra configs into the Buildroot config file.
    TODO Can't get rid of these for now with nice fragments:
    http://stackoverflow.com/questions/44078245/is-it-possible-to-use-config-fragments-with-buildroots-config
    """
    if buildroot_config_fragments is None:
        buildroot_config_fragments = []
    with open(common.buildroot_config_file, 'a') as br2_config_file:
        for buildroot_config_fragment in buildroot_config_fragments:
            with open(buildroot_config_fragment, 'r') as br2_config_fragment:
                for line in br2_config_fragment:
                    br2_config_file.write(line)
        for buildroot_config in buildroot_configs:
            br2_config_file.write(buildroot_config + '\n')

if __name__ == '__main__':
    parser = get_argparse()
    args = common.setup(parser)
    sys.exit(main(args))

path: build-buildroot


code:
#!/usr/bin/env bash
asciidoctor -o out/README.html -v README.adoc

path: build-doc


code:
#!/usr/bin/env python3

import glob
import multiprocessing
import os
import pathlib
import shutil
import subprocess

import common

parser = common.get_argparse()
common.add_build_arguments(parser)
parser.add_argument(
    'extra_scons_args',
    default=[],
    metavar='extra-scons-args',
    nargs='*'
)
args = common.setup(parser)
binaries_dir = os.path.join(common.gem5_system_dir, 'binaries')
disks_dir = os.path.join(common.gem5_system_dir, 'disks')
if args.clean:
    common.rmrf(common.gem5_build_dir)
else:
    os.makedirs(binaries_dir, exist_ok=True)
    os.makedirs(disks_dir, exist_ok=True)
    if  not os.path.exists(os.path.join(common.gem5_src_dir, '.git')):
        subprocess.check_call([
            'git',
            '-C', common.gem5_default_src_dir,
            'worktree', 'add',
            '-b', os.path.join('wt', args.gem5_build_id),
            common.gem5_src_dir
        ])
    if args.arch == 'x86_64':
        dummy_img_path = os.path.join(disks_dir, 'linux-bigswap2.img')
        with open(dummy_img_path, 'wb') as dummy_img_file:
            zeroes = b'\x00' * (2 ** 16)
            for i in range(2 ** 10):
                dummy_img_file.write(zeroes)
        subprocess.check_call(['mkswap', dummy_img_path])
        with open(os.path.join(binaries_dir, 'x86_64-vmlinux-2.6.22.9'), 'w'):
            # This file must always be present, despite --kernel overriding that default and selecting the kernel.
            # I'm not even joking. No one has ever built x86 gem5 without the magic dist dir present.
            pass
    elif args.arch == 'arm' or args.arch == 'aarch64':
        gem5_system_src_dir = os.path.join(common.gem5_src_dir, 'system')

        # dtb
        dt_src_dir = os.path.join(gem5_system_src_dir, 'arm', 'dt')
        dt_build_dir = os.path.join(common.gem5_system_dir, 'arm', 'dt')
        subprocess.check_call(['make', '-C', dt_src_dir])
        os.makedirs(dt_build_dir, exist_ok=True)
        for dt in glob.glob(os.path.join(dt_src_dir, '*.dtb')):
            shutil.copy2(dt, dt_build_dir)

        # Bootloader 32.
        bootloader32_dir = os.path.join(gem5_system_src_dir, 'arm', 'simple_bootloader')
        # TODO use the buildroot cross compiler here, and remove the dependencies from configure.
        subprocess.check_call(['make', '-C', bootloader32_dir])
        # bootloader
        shutil.copy2(os.path.join(bootloader32_dir, 'boot_emm.arm'), binaries_dir)

        # Bootloader 64.
        bootloader64_dir = os.path.join(gem5_system_src_dir, 'arm', 'aarch64_bootloader')
        # TODO cross_compile is ignored because the make does not use CC...
        subprocess.check_call(['make', '-C', bootloader64_dir])
        shutil.copy2(os.path.join(bootloader64_dir, 'boot_emm.arm64'), binaries_dir)
    assert common.run_cmd([
            'scons',
            # TODO factor with build.
            '-j', str(multiprocessing.cpu_count()),
            '--ignore-style',
            common.gem5_executable
        ] +
        args.extra_scons_args,
        cwd=common.gem5_src_dir,
        extra_env={'PATH': '/usr/lib/ccache:' + os.environ['PATH']},
    ) == 0
    term_src_dir = os.path.join(common.gem5_src_dir, 'util/term')
    subprocess.check_call(['make', '-C', term_src_dir])
    shutil.copy2(os.path.join(term_src_dir, 'm5term'), common.gem5_m5term)


code:
#!/usr/bin/env python3

import multiprocessing
import os
import subprocess

import common

parser = common.get_argparse()
common.add_build_arguments(parser)
parser.add_argument(
    'extra_config_args',
    default=[],
    metavar='extra-config-args',
    nargs='*'
)
args = common.setup(parser)
if args.clean:
    common.rmrf(common.qemu_build_dir)
else:
    os.makedirs(common.qemu_build_dir, exist_ok=True)
    subprocess.check_call(
        [
            os.path.join(common.qemu_src_dir, 'configure'),
            '--enable-debug',
            '--enable-trace-backends=simple',
            '--target-list={}-softmmu'.format(args.arch),
            '--enable-sdl',
            '--with-sdlabi=2.0',
        ] +
        args.extra_config_args,
        cwd=common.qemu_build_dir
    )
    subprocess.check_call(
        [
            'make',
            # TODO factor with build.
            '-j', str(multiprocessing.cpu_count()),
        ],
        cwd=common.qemu_build_dir
    )

path: build-qemu


code:
# Toolchain options.
# Enable as much visibility as possible.
BR2_CCACHE=y
BR2_CCACHE_USE_BASEDIR=n
BR2_DEBUG_3=y
BR2_ENABLE_DEBUG=y
BR2_GCC_ENABLE_GRAPHITE=y
BR2_GCC_ENABLE_LTO=y
BR2_GCC_ENABLE_OPENMP=y
BR2_OPTIMIZE_0=y
BR2_PTHREAD_DEBUG=y
BR2_TOOLCHAIN_BUILDROOT_CXX=y
BR2_TOOLCHAIN_BUILDROOT_FORTRAN=y
BR2_TOOLCHAIN_BUILDROOT_WCHAR=y

# Rootfs
BR2_TARGET_ROOTFS_CPIO=n
BR2_TARGET_ROOTFS_EXT2=y
BR2_TARGET_ROOTFS_INITRAMFS=n
BR2_TARGET_ROOTFS_EXT2_SIZE="512M"

# Host GDB
BR2_GDB_VERSION="7.11.1"
BR2_GDB_VERSION_7_10=n
BR2_GDB_VERSION_7_11=y
BR2_GDB_VERSION_7_12=n
BR2_PACKAGE_HOST_GDB=y
BR2_PACKAGE_HOST_GDB_PYTHON=y
BR2_PACKAGE_HOST_GDB_SIM=y
BR2_PACKAGE_HOST_GDB_TUI=y

# Custom packages
# Keepding those in because we control them fully
# and know for sure that are small.
BR2_PACKAGE_KERNEL_MODULES=y
BR2_SAMPLE_PACKAGE=y

# We were tempted to do this to disable S40network neatly,
# but that package also creates extra configuration files
# such as /etc/network/interfaces which we need. So we just
# remove the init.d file for now.
#BR2_PACKAGE_IFUPDOWN_SCRIPTS=n

# misc packages
BR2_PACKAGE_DHRYSTONE=y
BR2_PACKAGE_FILE=y
BR2_PACKAGE_PCIUTILS=y
BR2_PACKAGE_STRACE=y

# lscpu: TODO not installing?
BR2_PACKAGE_UTIL_LINUX=y
BR2_PACKAGE_UTIL_LINUX_BINARIES=y
# taskset
BR2_PACKAGE_UTIL_LINUX_SCHEDUTILS=y

# gdbserver
BR2_PACKAGE_GDB=y

# ftrace
BR2_PACKAGE_TRACE_CMD=y

# DTC
BR2_PACKAGE_DTC=y
BR2_PACKAGE_DTC_PROGRAMS=y
BR2_PACKAGE_HOST_DTC=y


code:
BR2_PACKAGE_KMSCUBE=y
BR2_PACKAGE_MESA3D=y
BR2_PACKAGE_MESA3D_OPENGL_EGL=y
BR2_PACKAGE_MESA3D_OPENGL_ES=y

# Software rasteriser.
# Selects the following chain:
# BR2_PACKAGE_MESA3D_DRIVER=y
# BR2_PACKAGE_MESA3D_DRI_DRIVER=y
BR2_PACKAGE_MESA3D_DRI_DRIVER_SWRAST=y


code:
BR2_PACKAGE_XAPP_TWM=y
BR2_PACKAGE_XAPP_XCALC=y
BR2_PACKAGE_XAPP_XCLOCK=y
BR2_PACKAGE_XAPP_XEYES=y
BR2_PACKAGE_XAPP_XINIT=y
BR2_PACKAGE_XDRIVER_XF86_INPUT_KEYBOARD=y
BR2_PACKAGE_XDRIVER_XF86_INPUT_MOUSE=y
BR2_PACKAGE_XDRIVER_XF86_VIDEO_CIRRUS=y
BR2_PACKAGE_XDRIVER_XF86_VIDEO_FBDEV=y
BR2_PACKAGE_XDRIVER_XF86_VIDEO_VESA=y
BR2_PACKAGE_XORG7=y
BR2_PACKAGE_XSERVER_XORG_SERVER=y
BR2_PACKAGE_XTERM=y
BR2_TOOLCHAIN_BUILDROOT_CXX=y
BR2_TOOLCHAIN_BUILDROOT_WCHAR=y


code:
LINUX_OVERRIDE_SRCDIR = ../../submodules/linux
QEMU_OVERRIDE_SRCDIR = ../../submodules/qemu


code:
CONFIG_BASE64=y
CONFIG_DEPMOD=y
CONFIG_MODINFO=y
CONFIG_NC=y
CONFIG_NC_EXTRA=y
CONFIG_NC_SERVER=y
CONFIG_STAT=y

# On ARM, with our lkmc_platform_device:
#
#     devmem 0x101e9000 w 0x12345678
#
# Then on QEMU monitor, notice that the registers don't actually change value:
#
#     xp/4 0x101e9000
#
# Uses /dev/mem.
#
# See also:
#
# - https://superuser.com/questions/71389/what-is-dev-mem/1214662#1214662
# - https://unix.stackexchange.com/questions/4948/shell-command-to-read-device-registers
# - man mem
#
# TODO: why with mmap MAP_PRIVATE (used in my previous custom naive version),
# the entire register page is read?;
#
# TODO: have a look at: https://github.com/kaiwan/device-memory-readwrite
CONFIG_DEVMEM=y


code:
#!/usr/bin/env python3

import argparse
import base64
import copy
import glob
import imp
import os
import re
import shlex
import shutil
import signal
import stat
import subprocess
import sys

this = sys.modules[__name__]

# Default paths.
root_dir = os.path.dirname(os.path.abspath(__file__))
data_dir = os.path.join(root_dir, 'data')
p9_dir = os.path.join(data_dir, '9p')
gem5_non_default_src_root_dir = os.path.join(data_dir, 'gem5')
out_dir = os.path.join(root_dir, 'out')
bench_boot = os.path.join(out_dir, 'bench-boot.txt')
dl_dir = os.path.join(out_dir, 'dl')
submodules_dir = os.path.join(root_dir, 'submodules')
buildroot_src_dir = os.path.join(submodules_dir, 'buildroot')
gem5_default_src_dir = os.path.join(submodules_dir, 'gem5')
linux_src_dir = os.path.join(submodules_dir, 'linux')
qemu_src_dir = os.path.join(submodules_dir, 'qemu')
parsec_benchmark_src_dir = os.path.join(submodules_dir, 'parsec-benchmark')

# Other default variables.
arch_map = {
    'a': 'arm',
    'A': 'aarch64',
    'x': 'x86_64',
}
arches = [arch_map[k] for k in arch_map]
gem5_cpt_prefix = '^cpt\.'
sha = subprocess.check_output(['git', '-C', root_dir, 'log', '-1', '--format=%H']).decode().rstrip()
config_file = os.path.join(data_dir, 'config')
if os.path.exists(config_file):
    config = imp.load_source('config', config_file)
    configs = {x:getattr(config, x) for x in dir(config) if not x.startswith('__')}

def base64_encode(string):
    return base64.b64encode(string.encode()).decode()

def gem_list_checkpoint_dirs():
    '''
    List checkpoint directory, oldest first.
    '''
    global this
    prefix_re = re.compile(this.gem5_cpt_prefix)
    files = list(filter(lambda x: os.path.isdir(os.path.join(this.m5out_dir, x)) and prefix_re.search(x), os.listdir(this.m5out_dir)))
    files.sort(key=lambda x: os.path.getmtime(os.path.join(this.m5out_dir, x)))
    return files

def get_argparse(default_args=None, argparse_args=None):
    '''
    Return an argument parser with common arguments set.

    :type default_args: Dict[str,str]
    :type argparse_args: Dict
    '''
    global this
    if default_args is None:
        default_args = {}
    if argparse_args is None:
        argparse_args = {}
    arch_choices = []
    for key in this.arch_map:
        arch_choices.append(key)
        arch_choices.append(this.arch_map[key])
    default_build_id = 'default'
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter,
        **argparse_args
    )
    parser.add_argument(
        '-a', '--arch', choices=arch_choices, default='x86_64',
        help='CPU architecture. Default: %(default)s'
    )
    parser.add_argument(
        '-g', '--gem5', default=False, action='store_true',
        help='Use gem5 instead of QEMU'
    )
    parser.add_argument(
        '-L', '--linux-build-id', default=default_build_id,
        help='Linux build ID. Allows you to keep multiple separate Linux builds. Default: %(default)s'
    )
    parser.add_argument(
        '-M', '--gem5-build-id', default=default_build_id,
        help='gem5 build ID. Allows you to keep multiple separate gem5 builds. Default: %(default)s'
    )
    parser.add_argument(
        '-N', '--gem5-worktree',
        help='''\
gem5 git worktree to use for build and Python scripts at runtime. Automatically
create a new git worktree with the given id if one does not exist. If not
given, just use the submodule source.
'''
    )
    parser.add_argument(
        '-n', '--run-id', default='0',
        help='''\
ID for run outputs such as gem5's m5out. Allows you to do multiple runs,
and then inspect separate outputs later in different output directories.
Default: %(default)s
'''
    )
    parser.add_argument(
        '--port-offset', type=int,
        help='''\
Increase the ports to be used such as for GDB by an offset to run multiple
instances in parallel.
Default: the run ID (-n) if that is an integer, otherwise 0.
'''
    )
    parser.add_argument(
        '-Q', '--qemu-build-id', default=default_build_id,
        help='QEMU build ID. Allows you to keep multiple separate QEMU builds. Default: %(default)s'
    )
    parser.add_argument(
        '--buildroot-build-id',
        default=default_build_id,
        help='Buildroot build ID. Allows you to keep multiple separate gem5 builds. Default: %(default)s'
    )
    parser.add_argument(
        '-t', '--gem5-build-type', default='opt',
        help='gem5 build type, most often used for "debug" builds. Default: %(default)s'
    )
    if hasattr(this, 'configs'):
        defaults = this.configs.copy()
    else:
        defaults = {}
    defaults.update(default_args)
    # A bit ugly as it actually changes the defaults shown on --help, but we can't do any better
    # because it is impossible to check if arguments were given or not...
    # - https://stackoverflow.com/questions/30487767/check-if-argparse-optional-argument-is-set-or-not
    # - https://stackoverflow.com/questions/3609852/which-is-the-best-way-to-allow-configuration-options-be-overridden-at-the-comman
    parser.set_defaults(**defaults)
    return parser

def add_build_arguments(parser):
    parser.add_argument(
        '--clean',
        help='Clean the build instead of building.',
        action='store_true',
    )

def get_elf_entry(elf_file_path):
    global this
    readelf_header = subprocess.check_output([
        this.get_toolchain_tool('readelf'),
        '-h',
        elf_file_path
    ])
    for line in readelf_header.decode().split('\n'):
        split = line.split()
        if line.startswith('  Entry point address:'):
            addr = line.split()[-1]
            break
    return int(addr, 0)

def get_stats(stat_re=None, stats_file=None):
    global this
    if stat_re is None:
        stat_re = '^system.cpu[0-9]*.numCycles$'
    if stats_file is None:
        stats_file = this.stats_file
    stat_re = re.compile(stat_re)
    ret = []
    with open(stats_file, 'r') as statfile:
        for line in statfile:
            if line[0] != '-':
                cols = line.split()
                if len(cols) > 1 and stat_re.search(cols[0]):
                    ret.append(cols[1])
    return ret

def get_toolchain_tool(tool):
    global this
    return glob.glob(os.path.join(this.host_bin_dir, '*-buildroot-*-{}'.format(tool)))[0]

def log_error(msg):
    print('error: {}'.format(msg), file=sys.stderr)

def print_cmd(cmd, cmd_file=None, extra_env=None):
    '''
    Format a command given as a list of strings so that it can
    be viewed nicely and executed by bash directly and print it to stdout.

    Optionally save the command to cmd_file file, and add extra_env
    environment variables to the command generated.
    '''
    newline_separator = ' \\\n'
    out = []
    for key in extra_env:
        out.extend(['{}={}'.format(shlex.quote(key), shlex.quote(extra_env[key])), newline_separator])
    for arg in cmd:
        out.extend([shlex.quote(arg), newline_separator])
    out = ''.join(out)
    print(out)
    if cmd_file is not None:
        with open(cmd_file, 'w') as f:
            f.write('#!/usr/bin/env bash\n')
            f.write(out)
        st = os.stat(cmd_file)
        os.chmod(cmd_file, st.st_mode | stat.S_IXUSR)

def resolve_args(defaults, args, extra_args):
    if extra_args is None:
        extra_args = {}
    argcopy = copy.copy(args)
    argcopy.__dict__ = dict(list(defaults.items()) + list(argcopy.__dict__.items()) + list(extra_args.items()))
    return argcopy

def rmrf(path):
    if os.path.exists(path):
        shutil.rmtree(path)

def run_cmd(
        cmd,
        cmd_file=None,
        out_file=None,
        show_stdout=True,
        show_cmd=True,
        extra_env=None,
        delete_env=None,
        **kwargs
    ):
    '''
    Run a command. Write the command to stdout before running it.

    Wait until the command finishes execution.

    :param cmd: command to run
    :type cmd: List[str]

    :param cmd_file: if not None, write the command to be run to that file
    :type cmd_file: str

    :param out_file: if not None, write the stdout and stderr of the command the file
    :type out_file: str

    :param show_stdout: wether to show stdout and stderr on the terminal or not
    :type show_stdout: bool

    :param extra_env: extra environment variables to add when running the command
    :type extra_env: Dict[str,str]
    '''
    if out_file is not None:
        stdout = subprocess.PIPE
        stderr = subprocess.STDOUT
    else:
        if show_stdout:
            stdout = None
            stderr = None
        else:
            stdout = subprocess.DEVNULL
            stderr = subprocess.DEVNULL
    if extra_env is None:
        extra_env = {}
    if delete_env is None:
        delete_env = []
    env = os.environ.copy()
    env.update(extra_env)
    for key in delete_env:
        del env[key]
    if show_cmd:
        print_cmd(cmd, cmd_file, extra_env=extra_env)
    # Otherwise Ctrl + C gives:
    # - ugly Python stack trace for gem5 (QEMU takes over terminal and is fine).
    # - kills Python, and that then kills GDB: https://stackoverflow.com/questions/19807134/does-python-always-raise-an-exception-if-you-do-ctrlc-when-a-subprocess-is-exec
    signal.signal(signal.SIGINT, signal.SIG_IGN)
    # https://stackoverflow.com/questions/15535240/python-popen-write-to-stdout-and-log-file-simultaneously/52090802#52090802
    with subprocess.Popen(cmd, stdout=stdout, stderr=stderr, env=env, **kwargs) as proc:
        if out_file is not None:
            with open(out_file, 'bw') as logfile:
                while True:
                    byte = proc.stdout.read(1)
                    if byte:
                        if show_stdout:
                            sys.stdout.buffer.write(byte)
                            sys.stdout.flush()
                        logfile.write(byte)
                    else:
                        break
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    return proc.returncode

def setup(parser, **extra_args):
    '''
    Parse the command line arguments, and setup several variables based on them.
    Typically done after getting inputs from the command line arguments.
    '''
    global this
    args = parser.parse_args()
    if args.arch in this.arch_map:
        args.arch = this.arch_map[args.arch]
    if args.arch == 'arm':
        this.armv = 7
        this.gem5_arch = 'ARM'
    elif args.arch == 'aarch64':
        this.armv = 8
        this.gem5_arch = 'ARM'
    elif args.arch == 'x86_64':
        this.gem5_arch = 'X86'
    this.buildroot_build_dir = os.path.join(this.out_dir, 'buildroot', args.arch, args.buildroot_build_id)
    this.buildroot_config_file = os.path.join(this.buildroot_build_dir, '.config')
    this.build_dir = os.path.join(this.buildroot_build_dir, 'build')
    this.linux_build_dir = os.path.join(this.build_dir, 'linux-custom')
    this.linux_variant_dir = '{}.{}'.format(this.linux_build_dir, args.linux_build_id)
    this.vmlinux = os.path.join(this.linux_variant_dir, "vmlinux")
    this.qemu_build_dir = os.path.join(this.out_dir, 'qemu', args.qemu_build_id)
    this.qemu_executable = os.path.join(this.qemu_build_dir, '{}-softmmu'.format(args.arch), 'qemu-system-{}'.format(args.arch))
    this.qemu_img_executable = os.path.join(this.qemu_build_dir, 'qemu-img')
    this.qemu_guest_build_dir = os.path.join(this.build_dir, 'qemu-custom')
    this.host_dir = os.path.join(this.buildroot_build_dir, 'host')
    this.host_bin_dir = os.path.join(this.host_dir, 'usr', 'bin')
    this.images_dir = os.path.join(this.buildroot_build_dir, 'images')
    this.ext2_file = os.path.join(this.images_dir, 'rootfs.ext2')
    this.qcow2_file = os.path.join(this.images_dir, 'rootfs.ext2.qcow2')
    this.staging_dir = os.path.join(this.buildroot_build_dir, 'staging')
    this.target_dir = os.path.join(this.buildroot_build_dir, 'target')
    this.run_dir_base = os.path.join(this.out_dir, 'run')
    this.gem5_run_dir = os.path.join(this.run_dir_base, 'gem5', args.arch, str(args.run_id))
    this.m5out_dir = os.path.join(this.gem5_run_dir, 'm5out')
    this.stats_file = os.path.join(this.m5out_dir, 'stats.txt')
    this.trace_txt_file = os.path.join(this.m5out_dir, 'trace.txt')
    this.gem5_readfile = os.path.join(this.gem5_run_dir, 'readfile')
    this.gem5_termout_file = os.path.join(this.gem5_run_dir, 'termout.txt')
    this.qemu_run_dir = os.path.join(this.run_dir_base, 'qemu', args.arch, str(args.run_id))
    this.qemu_trace_basename = 'trace.bin'
    this.qemu_trace_file = os.path.join(this.qemu_run_dir, 'trace.bin')
    this.qemu_trace_txt_file = os.path.join(this.qemu_run_dir, 'trace.txt')
    this.qemu_termout_file = os.path.join(this.qemu_run_dir, 'termout.txt')
    this.qemu_rrfile = os.path.join(this.qemu_run_dir, 'rrfile')
    this.gem5_build_dir = os.path.join(this.out_dir, 'gem5', args.gem5_build_id)
    this.gem5_m5term = os.path.join(this.gem5_build_dir, 'm5term')
    this.gem5_build_build_dir = os.path.join(this.gem5_build_dir, 'build')
    this.gem5_executable = os.path.join(this.gem5_build_build_dir, gem5_arch, 'gem5.{}'.format(args.gem5_build_type))
    this.gem5_system_dir = os.path.join(this.gem5_build_dir, 'system')
    if args.gem5_worktree is not None:
        this.gem5_src_dir = os.path.join(this.gem5_non_default_src_root_dir, args.gem5_worktree)
    else:
        this.gem5_src_dir = this.gem5_default_src_dir
    if args.gem5:
        this.executable = this.gem5_executable
        this.run_dir = this.gem5_run_dir
        this.termout_file = this.gem5_termout_file
    else:
        this.executable = this.qemu_executable
        this.run_dir = this.qemu_run_dir
        this.termout_file = this.qemu_termout_file
    this.gem5_config_dir = os.path.join(this.gem5_src_dir, 'configs')
    this.gem5_se_file = os.path.join(this.gem5_config_dir, 'example', 'se.py')
    this.gem5_fs_file = os.path.join(this.gem5_config_dir, 'example', 'fs.py')
    this.run_cmd_file = os.path.join(this.run_dir, 'run.sh')
    if args.arch == 'arm':
        this.linux_image = os.path.join('arch', 'arm', 'boot', 'zImage')
    elif args.arch == 'aarch64':
        this.linux_image = os.path.join('arch', 'arm64', 'boot', 'Image')
    elif args.arch == 'x86_64':
        this.linux_image = os.path.join('arch', 'x86', 'boot', 'bzImage')
    this.linux_image = os.path.join(this.linux_variant_dir, linux_image)

    # Ports.
    if args.port_offset is None:
        try:
            args.port_offset = int(args.run_id)
        except ValueError:
            args.port_offset = 0
    if args.gem5:
        this.gem5_telnet_port = 3456 + args.port_offset
        this.gdb_port = 7000 + args.port_offset
    else:
        this.qemu_base_port = 45454 + 10 * args.port_offset
        this.qemu_monitor_port = this.qemu_base_port + 0
        this.qemu_hostfwd_generic_port = this.qemu_base_port + 1
        this.qemu_hostfwd_ssh_port = this.qemu_base_port + 2
        this.qemu_gdb_port = this.qemu_base_port + 3
        this.gdb_port = this.qemu_gdb_port
    return args

def mkdir():
    global this
    os.makedirs(this.build_dir, exist_ok=True)
    os.makedirs(this.gem5_build_dir, exist_ok=True)
    os.makedirs(this.gem5_run_dir, exist_ok=True)
    os.makedirs(this.qemu_run_dir, exist_ok=True)
    os.makedirs(this.p9_dir, exist_ok=True)


code:
#!/usr/bin/env python
arch = 'aarch64'
gem5 = True
run_id = 'asdf'


code:
#!/usr/bin/env bash
set -eu
interactive_pkgs=libsdl2-dev
gem5=false
qemu=true
submodules='buildroot linux'
y=
while getopts gpqt OPT; do
  case "$OPT" in
    g)
      gem5=true
      ;;
    p)
      submodules="${submodules} parsec-benchmark"
      ;;
    q)
      qemu=false
      ;;
    t)
      interactive_pkgs=
      y=-y
      ;;
  esac
done
shift $(($OPTIND - 1))

## apt-get

pkgs="\
automake \
bc \
build-essential \
coreutils \
cpio \
expect \
git \
moreutils \
tmux \
unzip \
vinagre \
wget \
"
if "$gem5"; then
  pkgs="$pkgs \
ccache \
gcc-aarch64-linux-gnu \
gcc-arm-linux-gnueabi \
libgoogle-perftools-dev \
protobuf-compiler \
python-dev \
python-pip \
scons \
"
fi
command -v apt-get >/dev/null 2>&1 || {
  cat <<EOF
apt-get not found. You're on your own for installing dependencies.

On Ubuntu they are:

$pkgs
EOF
  exit 0
}

# Without this started failing in kernel 4.15 with:
# Makefile:932: *** "Cannot generate ORC metadata for CONFIG_UNWINDER_ORC=y, please install libelf-dev, libelf-devel or elfutils-libelf-devel".  Stop.
pkgs="$pkgs libelf-dev"

# https://stackoverflow.com/questions/20010199/determining-if-a-process-runs-inside-lxc-docker
if [ -f /.dockerenv ]; then
  # --jobs is not available in git 2.7.4 from Ubuntu 16.04.
  gitjobs=
  mysudo=
  # https://askubuntu.com/questions/496549/error-you-must-put-some-source-uris-in-your-sources-list
  sed -Ei 's/^# deb-src/deb-src/' /etc/apt/sources.list
  y=-y
else
  gitjobs="--jobs $(nproc)"
  mysudo=sudo
fi
$mysudo apt-get update $y
# Building SDL for QEMU in Buildroot was rejected upstream because it adds many dependencies:
# https://patchwork.ozlabs.org/patch/770684/
# We are just using the host SDL for now, if it causes too much problems we might remove it.
# libsdl2-dev needs to be installed separatedly from sudo apt-get build-dep qemu
# because Ubuntu 16.04's QEMU uses SDL 1.
$mysudo apt-get install $y \
  $pkgs \
  $interactive_pkgs \
;
if "$qemu"; then
  $mysudo apt-get build-dep $y qemu
fi
if "$gem5"; then
  # Generate graphs of config.ini under m5out.
  pip install --user pydot
fi

## Submodules

if "$qemu"; then
  submodules="${submodules} qemu"
fi
if "$gem5"; then
  submodules="${submodules} gem5"
fi
submodules="$(for submodule in ${submodules}; do printf "submodules/${submodule} "; done)"
(
  set -e
  # Shallow cloning saves a considerable amount of time, specially because of the linux kernel.
  # However, git submodules are buggy as usual, and this is the best way I've found to get it done:
  # https://stackoverflow.com/questions/2144406/git-shallow-submodules/47374702#47374702
  # In particular:
  # - `shallow = true` on the submodule has no effect for the non default educational branches of our submodules
  # - QEMU's submodules point to commits that are neither under branches nor tags, and so `--shallow-submodules` fails
  git submodule update --depth 1 $gitjobs --init -- ${submodules}
  if "$qemu"; then
    git -C submodules/qemu submodule update --init --recursive
  fi
) &
# https://unix.stackexchange.com/questions/65532/why-does-set-e-not-work-inside-subshells-with-parenthesis-followed-by-an-or
wait $! || git submodule update --init --recursive -- $submodules

path: configure


code:
#!/usr/bin/env bash
# echo a command and eval it.
# Can also print the command to a file.
set -e
a=
while getopts a OPT; do
  case "$OPT" in
    a)
      # Append to file instead of overwriting.
      a=-a
      ;;
    ?)
      exit 2
      ;;
  esac
done
shift "$(($OPTIND - 1))"
cmd="$1"
outfile="${2:-/dev/null}"
mkdir -p "$(dirname "$outfile")"
echo "$cmd" | tee $a "$outfile"
# PIPESTATUS so that cmd="main_cmd | post_process" will return the status of cmd.
# Not POSIX.
# https://unix.stackexchange.com/questions/14270/get-exit-status-of-process-thats-piped-to-another
# https://stackoverflow.com/questions/1221833/pipe-output-and-capture-exit-status-in-bash
eval "${cmd}; cmd_exit=\${PIPESTATUS[0]}"
exit "$cmd_exit"

path: eeval


code:
#!/usr/bin/env bash
set -eu
root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
generate_checkpoints=true
while getopts "C" OPT; do
  case "$OPT" in
    C)
      generate_checkpoints=false
      ;;
    ?)
      exit 2;
      ;;
  esac
done
shift "$(($OPTIND - 1))"
common_opts="--gem5 $@"

# Vars
cmd="./run ${common_opts}"
cache_small='--caches --l2cache --l1d_size=1024   --l1i_size=1024   --l2_size=1024   --l3_size=1024  '
cache_large='--caches --l2cache --l1d_size=1024kB --l1i_size=1024kB --l2_size=1024kB --l3_size=1024kB'
results_file="$(${root_dir}/getvar ${common_opts} run_dir)/bench-cache.txt"

bench() (
  "${root_dir}/bench-cmd" "$1"
  {
    printf 'cycles '
    ./gem5-stat ${common_opts}
    printf 'instructions '
    ./gem5-stat ${common_opts} sim_insts
    # RESTORE_INVESTIGATION
    #cycles_switch="$(./gem5-stat ${common_opts} system.switch_cpus.numCycles)"
    #if [ -n "$cycles_switch" ]; then
    #  printf "cycles_switch ${cycles_switch}\n"
    #fi
    printf "\n"
  } >> "$results_file"
)

bench-all() (
  bench "${cmd} --gem5-readfile \"$1\" --gem5-restore 1 -- ${cache_small} --cpu-type=HPI --restore-with-cpu=HPI"
  bench "${cmd} --gem5-readfile \"$1\" --gem5-restore 1 -- ${cache_large} --cpu-type=HPI --restore-with-cpu=HPI"
  # RESTORE_INVESTIGATION
  # These were mostly to investigate what happens on restore:
  # https://stackoverflow.com/questions/49011096/how-to-switch-cpu-models-in-gem5-after-restoring-a-checkpoint-and-then-observe-t
  #bench "${cmd} --gem5-readfile '$1' --gem5-restore 1"
  #bench "${cmd} --gem5-readfile '$1' --gem5-restore 1 -- ${cache_small}"
  #bench "${cmd} --gem5-readfile '$1' --gem5-restore 1 -- ${cache_large}"
  #bench "${cmd} --gem5-readfile '$1' --gem5-restore 2 -- ${cache_small}"
  #bench "${cmd} --gem5-readfile '$1' --gem5-restore 3 -- ${cache_large}"
  #bench "${cmd} --gem5-readfile '$1' --gem5-restore 4 -- ${cache_small} --cpu-type=HPI"
  #bench "${cmd} --gem5-readfile '$1' --gem5-restore 5 -- ${cache_large} --cpu-type=HPI"
  ## Restore from At-- omicSimpleCPU to HPI.
  #bench "${cmd} --gem5-readfile '$1' --gem5-restore 2 -- ${cache_small} --cpu-type=HPI --restore-with-cpu=HPI"
  #bench "${cmd} --gem5-readfile '$1' --gem5-restore 3 -- ${cache_large} --cpu-type=HPI --restore-with-cpu=HPI"
  #bench "${cmd} --gem5-readfile '$1' --gem5-restore 2 -- ${cache_small} --restore-with-cpu=HPI"
  #bench "${cmd} --gem5-readfile '$1' --gem5-restore 3 -- ${cache_large} --restore-with-cpu=HPI"
  #bench "${cmd} --gem5-readfile '$1' --gem5-restore 2 -- ${cache_small} --cpu-type=HPI"
  #bench "${cmd} --gem5-readfile '$1' --gem5-restore 3 -- ${cache_large} --cpu-type=HPI"
  ## Restore HPI with different cache sizes and see if it is used.
  #bench "${cmd} --gem5-readfile '$1' --gem5-restore 4 -- ${cache_large} --cpu-type=HPI"
  #bench "${cmd} --gem5-readfile '$1' --gem5-restore 5 -- ${cache_small} --cpu-type=HPI"
  #bench "${cmd} --gem5-readfile '$1' --gem5-restore 2 -- ${cache_large} --cpu-type=HPI"
  #bench "${cmd} --gem5-readfile '$1' --gem5-restore 3 -- ${cache_small} --cpu-type=HPI"
)

if "$generate_checkpoints"; then
  # Create the checkpoints after the kernel boot.
  cpt_cmd="-E '/gem5.sh'"
  # RESTORE_INVESTIGATION
  ## 5
  #./eeval "$cmd $cpt_cmd -- $cache_large --cpu-type=HPI"
  ## 4
  #./eeval "$cmd $cpt_cmd -- $cache_small --cpu-type=HPI"
  ## 3
  #./eeval "$cmd $cpt_cmd -- $cache_large"
  ## 2
  #./eeval "$cmd $cpt_cmd -- $cache_small"
  # 1
  ./eeval "$cmd $cpt_cmd"
fi

# Restore and run benchmarks.
rm -f "$results_file"
for n in 1000 10000 100000; do
  bench-all "dhrystone ${n}"
done


code:
#!/usr/bin/env bash

# https://github.com/cirosantilli/linux-kernel-module-cheat/tree/58de3f7243016c052ad080f82dd757d61878219b#gem5-run-benchmark

set -eu
root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
outfile="${root_dir}/out/gem5-bench-dhrystone.txt"
arch=aarch64
cmd="./run -a '$arch' --gem5 --eval-busybox '/gem5.sh'"

# These cache sizes roughly match the ARM Cortex A75
# https://en.wikipedia.org/wiki/ARM_Cortex-A75
restore='-l 1 -- --cpu-type=HPI --restore-with-cpu=HPI --caches --l2cache --l1d_size=128kB --l1i_size=1024kB --l2_size=256kB'

# Generate a checkpoint after Linux boots, using the faster and less detailed CPU.
# The boot takes a while, be patient young Padawan.
eval "$cmd"

printf 'n cycles\n' > "$outfile"
for n in 1000 10000 100000; do
  # Restore the most recent checkpoint taken with the more detailed and slower HPI CPU,
  # and run the benchmark with different parameters. We skip the boot completely, saving time!
  eval "${cmd} --gem5-readfile 'dhrystone ${n}' ${restore}" &>/dev/null
  printf "${n} " >> "$outfile"
  ./gem5-stat -a "$arch" >> "$outfile"
done


code:
#!/usr/bin/env python3

import subprocess
import sys

import common

parser = common.get_argparse(
    default_args={'gem5':True},
    argparse_args={'description':'Connect a terminal to a running gem5 instance'}
)
args = common.setup(parser)
sys.exit(common.run_cmd([str(common.gem5_m5term), 'localhost', str(common.gem5_telnet_port)]))


code:
#!/usr/bin/env python3
import common
parser = common.get_argparse(
    argparse_args={'description':'Get the value of a gem5 stat from the stats.txt file.'}
)
parser.add_argument(
    'stat',
    default=None,
    help='Python regexp matching the full stat name of interest',
    nargs='?',
)
args = common.setup(parser)
stats = common.get_stats(args.stat)
print('\n'.join(stats))


code:
#!/usr/bin/env python3
import common
parser = common.get_argparse(argparse_args={
    'description': '''Print the value of a common.py variable.

This is useful to:

* give dry commands on the README that don't change when we refactor directory structure
* create simple bash scripts that call use common.py variables

For example, to get the Buildroot output directory for an ARM build, use:

....
./%(prog)s -a arm buildroot_build_dir
....

'''
})
parser.add_argument('variable')
args = common.setup(parser)
print(getattr(common, args.variable))

path: getvar


code:
obj-m += hello.o
ccflags-y := -Wno-declaration-after-statement -std=gnu99

.PHONY: all clean

all: hello.ko

hello.ko: hello.c
	$(MAKE) -C '/lib/modules/$(shell uname -r)/build' M='$(PWD)' modules

clean:
	$(MAKE) -C '/lib/modules/$(shell uname -r)/build' M='$(PWD)' clean


code:
#include <linux/module.h>
#include <linux/kernel.h>

static int myinit(void)
{
	pr_info("hello init\n");
	return 0;
}

static void myexit(void)
{
	pr_info("hello exit\n");
}

module_init(myinit)
module_exit(myexit)
MODULE_LICENSE("GPL");


code:
# Misc.
CONFIG_BLK_DEV_INITRD=y
CONFIG_DEBUG_FS=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_MODULE_SRCVERSION_ALL=y
CONFIG_OVERLAY_FS=y
CONFIG_STRICT_DEVMEM=n

# GDB debugging.
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_KERNEL=y
CONFIG_GDB_SCRIPTS=y

# KGDB
CONFIG_CONSOLE_POLL=y
CONFIG_KDB_CONTINUE_CATASTROPHIC=0
CONFIG_KDB_DEFAULT_ENABLE=0x1
CONFIG_KDB_KEYBOARD=y
CONFIG_KGDB=y
CONFIG_KGDB_KDB=y
CONFIG_KGDB_LOW_LEVEL_TRAP=y
CONFIG_KGDB_SERIAL_CONSOLE=y
CONFIG_KGDB_TESTS=y
CONFIG_KGDB_TESTS_ON_BOOT=n
CONFIG_MAGIC_SYSRQ=y
CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1
CONFIG_SERIAL_KGDB_NMI=n

# Non-static variables show up on /proc/kallsyms
# https://stackoverflow.com/questions/20196636/does-kallsyms-have-all-the-symbol-of-kernel-functions/44614878#44614878
CONFIG_KALLSYMS_ALL=y

# /proc/config.gz
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y

# TODO make example.
# This seems to allow userspace to create arbitrary configuration trees,
# which kernel modules can then read and interpret.
CONFIG_CONFIGFS_FS=y

# Module.symvers in kernel tree and modules tree contains CRC of signatures.
# TODO: I think the CRC are stored in the built kernel and module, and checked
# at insmod, but bgrep did not find it in kernel image.
# Does not show in /proc/kallyms either.
CONFIG_MODVERSIONS=y

# ftrace
CONFIG_DYNAMIC_FTRACE=y
CONFIG_FTRACE=y
CONFIG_FTRACE_SYSCALLS=y
CONFIG_FUNCTION_GRAPH_TRACER=y
CONFIG_FUNCTION_PROFILER=y
CONFIG_FUNCTION_TRACER=y
CONFIG_HWLAT_TRACER=y
CONFIG_IRQSOFF_TRACER=y
CONFIG_SCHED_TRACER=y
CONFIG_STACK_TRACER=y
CONFIG_TRACER_SNAPSHOT=y

# Process tracing.
CONFIG_CONNECTOR=y
CONFIG_PROC_EVENTS=y

# 9P
CONFIG_9P_FS=y
CONFIG_9P_FS_POSIX_ACL=y
CONFIG_9P_FS_SECURITY=y
CONFIG_NETWORK_FILESYSTEMS=y
CONFIG_NET_9P=y
CONFIG_NET_9P_DEBUG=y
CONFIG_NET_9P_VIRTIO=y
# 9P needed for ARM. Not fully minimized, but so be it.
CONFIG_PCI=y
CONFIG_PCI_HOST_COMMON=y
CONFIG_PCI_HOST_GENERIC=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_BLK=y

# Misc
CONFIG_DUMMY_IRQ=m
CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
# Requirement for LOGO.
CONFIG_FB=y
CONFIG_LOGO=y

## Networking

# Will everything blow up?
# https://superuser.com/questions/684005/how-does-one-permanently-disable-gnu-linux-networking/1255015#1255015
#CONFIG_NET=n

# If given, we can use QEMU 2.9.0 default x86 networking without any -net or -netdev options,
# since E1000 is the default networking device as mentioned at:
# https://en.wikibooks.org/w/index.php?title=QEMU/Networking&oldid=3268753
#CONFIG_E1000=y

## x86

# https://stackoverflow.com/questions/20069620/print-kernels-page-table-entries
# cat /sys/kernel/debug/kernel_page_tables
CONFIG_X86_PTDUMP=y

## UIO

# All other UIO depend on this module.
CONFIG_UIO=m

# Generic platform devices.
# https://yurovsky.github.io/2014/10/10/linux-uio-gpio-interrupt/
CONFIG_UIO_DMEM_GENIRQ=m
CONFIG_UIO_PDRV_GENIRQ=m

# https://github.com/rumpkernel/wiki/wiki/Howto:-Accessing-PCI-devices-from-userspace
# /sys/class/uio/
# /sys/class/uio/uio0
CONFIG_UIO_PCI_GENERIC=m

## ARM

# Like CONFIG_X86_PTDUMP for ARM.
CONFIG_ARM64_PTDUMP=y

# For record and replay.
CONFIG_8139CP=y


code:
CONFIG_DRM=y

## QEMU
CONFIG_DRM_VIRTIO_GPU=y

## gem5
CONFIG_DRM_HDLCD=y
# Only present in the gem5 fork.
CONFIG_DRM_VIRT_ENCODER=y


code:
CONFIG_ARM_AMBA=y
CONFIG_GPIOLIB=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_PL061=y