1 /*
2 * Copyright (C) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "dumper.h"
17 #include <string>
18 #include <unistd.h>
19 #include <cstdio>
20 #include <mutex>
21 #include <sys/time.h>
22 #include <securec.h>
23 #include <unordered_map>
24 #include "directory_ex.h"
25 #include "media_errors.h"
26 #include "media_log.h"
27 #include "param_wrapper.h"
28
29 namespace {
30 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN, "GstDumper"};
31 static std::mutex g_padBufCountMutex;
32 static std::unordered_map<GstPad *, uint64_t> g_padBufCount = {};
33 }
34
35 namespace OHOS {
36 namespace Media {
DumpDotGraph(GstPipeline & pipeline,int32_t oldState,int32_t newState)37 void Dumper::DumpDotGraph(GstPipeline &pipeline, int32_t oldState, int32_t newState)
38 {
39 if ((oldState < GST_STATE_VOID_PENDING) || (oldState > GST_STATE_PLAYING) ||
40 (newState < GST_STATE_VOID_PENDING) || (newState > GST_STATE_PLAYING)) {
41 MEDIA_LOGE("invalid state, oldState: %{public}d, newState: %{public}d", oldState, newState);
42 return;
43 }
44
45 std::string dumpDir;
46 int res = OHOS::system::GetStringParameter("sys.media.dump.dot.path", dumpDir, "");
47 if (res != 0 || dumpDir.empty()) {
48 return;
49 }
50
51 std::string realPath;
52 CHECK_AND_RETURN_LOG(PathToRealPath(dumpDir, realPath), "invalid dump path: %{public}s", dumpDir.c_str());
53
54 struct timeval tv;
55 int ret = gettimeofday(&tv, nullptr);
56 CHECK_AND_RETURN_LOG(ret >= 0, "get time of day failed");
57
58 constexpr int32_t secPerHour = 60 * 60;
59 constexpr int32_t minutePerHour = 60;
60 constexpr int32_t secPerMinute = 60;
61 constexpr int32_t usecPerMSec = 1000;
62
63 uint64_t hour = static_cast<uint64_t>(tv.tv_sec / secPerHour);
64 uint32_t minute = static_cast<uint32_t>((tv.tv_sec / secPerMinute) % minutePerHour);
65 uint32_t sec = static_cast<uint32_t>(tv.tv_sec % minutePerHour);
66 uint32_t millsec = static_cast<uint32_t>(tv.tv_usec / usecPerMSec);
67
68 const gchar *oldName = gst_element_state_get_name(static_cast<GstState>(oldState));
69 const gchar *newName = gst_element_state_get_name(static_cast<GstState>(newState));
70
71 char fullPath[PATH_MAX] = { 0 };
72 const char *format = "%s/%" PRIu64 ".%u.%u.%u-media-pipeline.0x%06" PRIXPTR ".%s_%s.dot";
73 ret = sprintf_s(fullPath, PATH_MAX, format, realPath.c_str(), hour, minute, sec, millsec,
74 FAKE_POINTER(&pipeline), oldName, newName);
75 if (ret <= 0) {
76 MEDIA_LOGE("dump dot failed for 0x%{public}06" PRIXPTR " %{public}s to %{public}s",
77 FAKE_POINTER(&pipeline), oldName, newName);
78 return;
79 }
80
81 FILE *fp = fopen(fullPath, "wb");
82 CHECK_AND_RETURN_LOG(fp != nullptr, "open path failed, %{public}s", fullPath);
83
84 gchar *buf = gst_debug_bin_to_dot_data(GST_BIN(&pipeline), GST_DEBUG_GRAPH_SHOW_ALL);
85 if (buf != nullptr) {
86 (void)fputs(buf, fp);
87 g_free(buf);
88 } else {
89 MEDIA_LOGD("buf is nullptr");
90 }
91
92 (void)fclose(fp);
93 MEDIA_LOGD("wrote pipeline graph to : '%{public}s'", fullPath);
94 }
95
DumpGstBuffer(GstPad * pad,GstPadProbeInfo * info,gpointer user_data)96 GstPadProbeReturn Dumper::DumpGstBuffer(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
97 {
98 (void)user_data;
99 CHECK_AND_RETURN_RET(pad != nullptr && info != nullptr, GST_PAD_PROBE_OK);
100
101 if ((GST_PAD_PROBE_INFO_TYPE(info) & GST_PAD_PROBE_TYPE_BUFFER) == 0) {
102 return GST_PAD_PROBE_OK;
103 }
104
105 GstBuffer *buf = gst_pad_probe_info_get_buffer(info);
106 CHECK_AND_RETURN_RET(buf != nullptr, GST_PAD_PROBE_OK);
107 GstMapInfo mapInfo = GST_MAP_INFO_INIT;
108 CHECK_AND_RETURN_RET(gst_buffer_map(buf, &mapInfo, GST_MAP_READ), GST_PAD_PROBE_OK);
109
110 uint64_t bufSeq = 0;
111 {
112 std::lock_guard<std::mutex> lock(g_padBufCountMutex);
113 if (g_padBufCount.count(pad) == 0) {
114 g_padBufCount.emplace(pad, 0);
115 }
116 bufSeq = g_padBufCount[pad]++;
117 }
118
119 char fullPath[PATH_MAX] = { 0 };
120 const char *format = "/data/media/dump/pad_%s_%s_buf_%" PRIu64 "";
121 if (sprintf_s(fullPath, PATH_MAX, format, GST_DEBUG_PAD_NAME(pad), bufSeq) <= 0) {
122 MEDIA_LOGE("dump buffer failed for 0x%{public}06" PRIXPTR ", pad is %{public}s:%{public}s",
123 FAKE_POINTER(buf), GST_DEBUG_PAD_NAME(pad));
124 gst_buffer_unmap(buf, &mapInfo);
125 return GST_PAD_PROBE_OK;
126 }
127
128 FILE *fp = fopen(fullPath, "wb");
129 if (fp != nullptr) {
130 (void)fwrite(mapInfo.data, mapInfo.size, 1, fp);
131 (void)fflush(fp);
132 (void)fclose(fp);
133 fp = nullptr;
134 } else {
135 MEDIA_LOGE("open path failed, %{public}s", fullPath);
136 }
137
138 gst_buffer_unmap(buf, &mapInfo);
139
140 MEDIA_LOGD("wrote buffer to %{public}s", fullPath);
141 return GST_PAD_PROBE_OK;
142 }
143
AddDumpGstBufferProbe(GstElement * element,const gchar * padname)144 void Dumper::AddDumpGstBufferProbe(GstElement *element, const gchar *padname)
145 {
146 GstPad *pad = gst_element_get_static_pad(element, padname);
147 CHECK_AND_RETURN(pad != nullptr);
148
149 gulong ret = gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, &Dumper::DumpGstBuffer, nullptr, nullptr);
150 if (ret == 0) {
151 MEDIA_LOGE("add dump gst buffer probe to pad %{public}s:%{public}s failed", GST_DEBUG_PAD_NAME(pad));
152 } else {
153 MEDIA_LOGD("add dump gst buffer probe to pad %{public}s:%{public}s success", GST_DEBUG_PAD_NAME(pad));
154 }
155
156 gst_object_unref(pad);
157 }
158
IsEnableDumpGstBuffer()159 bool Dumper::IsEnableDumpGstBuffer()
160 {
161 int value = OHOS::system::GetIntParameter("sys.media.dump.gstbuffer", 0);
162 if (value == 0) {
163 return false;
164 }
165
166 return true;
167 }
168 } // namespace Media
169 } // namespace OHOS
170